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