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