1 <?php
2 
3 namespace dokuwiki\Debug;
4 
5 use dokuwiki\Extension\Event;
6 use dokuwiki\Extension\EventHandler;
7 use dokuwiki\Logger;
8 
9 class DebugHelper
10 {
11     protected const INFO_DEPRECATION_LOG_EVENT = 'INFO_DEPRECATION_LOG';
12 
13     /**
14      * Check if deprecation messages shall be handled
15      *
16      * This is either because its logging is not disabled or a deprecation handler was registered
17      *
18      * @return bool
19      */
20     public static function isEnabled()
21     {
22         /** @var EventHandler $EVENT_HANDLER */
23         global $EVENT_HANDLER;
24         if (
25             !Logger::getInstance(Logger::LOG_DEPRECATED)->isLogging() &&
26             (!$EVENT_HANDLER instanceof EventHandler || !$EVENT_HANDLER->hasHandlerForEvent('INFO_DEPRECATION_LOG'))
27         ) {
28             // avoid any work if no one cares
29             return false;
30         }
31         return true;
32     }
33 
34     /**
35      * Log accesses to deprecated fucntions to the debug log
36      *
37      * @param string $alternative (optional) The function or method that should be used instead
38      * @param int $callerOffset (optional) How far the deprecated method is removed from this one
39      * @param string $thing (optional) The deprecated thing, defaults to the calling method
40      * @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
41      */
42     public static function dbgDeprecatedFunction($alternative = '', $callerOffset = 1, $thing = '')
43     {
44         if (!self::isEnabled()) return;
45 
46         $backtrace = debug_backtrace();
47         for ($i = 0; $i < $callerOffset; ++$i) {
48             if (count($backtrace) > 1) array_shift($backtrace);
49         }
50 
51         [$self, $call] = $backtrace;
52 
53         self::triggerDeprecationEvent(
54             $backtrace,
55             $alternative,
56             self::formatCall($self),
57             self::formatCall($call),
58             $self['file'] ?? $call['file'] ?? '',
59             $self['line'] ?? $call['line'] ?? 0
60         );
61     }
62 
63     /**
64      * Format the given backtrace info into a proper function/method call string
65      * @param array $call
66      * @return string
67      */
68     protected static function formatCall($call)
69     {
70         $thing = '';
71         if (!empty($call['class'])) {
72             $thing .= $call['class'] . '::';
73         }
74         $thing .= $call['function'] . '()';
75         return trim($thing, ':');
76     }
77 
78     /**
79      * This marks logs a deprecation warning for a property that should no longer be used
80      *
81      * This is usually called withing a magic getter or setter.
82      * For logging deprecated functions or methods see dbgDeprecatedFunction()
83      *
84      * @param string $class The class with the deprecated property
85      * @param string $propertyName The name of the deprecated property
86      *
87      * @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
88      */
89     public static function dbgDeprecatedProperty($class, $propertyName)
90     {
91         if (!self::isEnabled()) return;
92 
93         $backtrace = debug_backtrace();
94         array_shift($backtrace);
95         $call = $backtrace[1];
96         $caller = trim($call['class'] . '::' . $call['function'] . '()', ':');
97         $qualifiedName = $class . '::$' . $propertyName;
98         self::triggerDeprecationEvent(
99             $backtrace,
100             '',
101             $qualifiedName,
102             $caller,
103             $backtrace[0]['file'],
104             $backtrace[0]['line']
105         );
106     }
107 
108     /**
109      * Trigger a custom deprecation event
110      *
111      * Usually dbgDeprecatedFunction() or dbgDeprecatedProperty() should be used instead.
112      * This method is intended only for those situation where they are not applicable.
113      *
114      * @param string $alternative
115      * @param string $deprecatedThing
116      * @param string $caller
117      * @param string $file
118      * @param int $line
119      * @param int $callerOffset How many lines should be removed from the beginning of the backtrace
120      */
121     public static function dbgCustomDeprecationEvent(
122         $alternative,
123         $deprecatedThing,
124         $caller,
125         $file,
126         $line,
127         $callerOffset = 1
128     ) {
129         if (!self::isEnabled()) return;
130 
131         $backtrace = array_slice(debug_backtrace(), $callerOffset);
132 
133         self::triggerDeprecationEvent(
134             $backtrace,
135             $alternative,
136             $deprecatedThing,
137             $caller,
138             $file,
139             $line
140         );
141     }
142 
143     /**
144      * @param array $backtrace
145      * @param string $alternative
146      * @param string $deprecatedThing
147      * @param string $caller
148      * @param string $file
149      * @param int $line
150      */
151     private static function triggerDeprecationEvent(
152         array $backtrace,
153         $alternative,
154         $deprecatedThing,
155         $caller,
156         $file,
157         $line
158     ) {
159         $data = [
160             'trace' => $backtrace,
161             'alternative' => $alternative,
162             'called' => $deprecatedThing,
163             'caller' => $caller,
164             'file' => $file,
165             'line' => $line,
166         ];
167         $event = new Event(self::INFO_DEPRECATION_LOG_EVENT, $data);
168         if ($event->advise_before()) {
169             $msg = $event->data['called'] . ' is deprecated. It was called from ';
170             $msg .= $event->data['caller'] . ' in ' . $event->data['file'] . ':' . $event->data['line'];
171             if ($event->data['alternative']) {
172                 $msg .= ' ' . $event->data['alternative'] . ' should be used instead!';
173             }
174             Logger::getInstance(Logger::LOG_DEPRECATED)->log($msg);
175         }
176         $event->advise_after();
177     }
178 }
179