1<?php 2 3namespace dokuwiki\Debug; 4 5use dokuwiki\Extension\Event; 6use dokuwiki\Extension\EventHandler; 7use dokuwiki\Logger; 8 9class 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