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