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