[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * A tool for running hook functions. 5 * 6 * Copyright 2004, 2005 Evan Prodromou <[email protected]>. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 * 22 * @author Evan Prodromou <[email protected]> 23 * @see hooks.txt 24 * @file 25 */ 26 27 /** 28 * @since 1.18 29 */ 30 class MWHookException extends MWException { 31 } 32 33 /** 34 * Hooks class. 35 * 36 * Used to supersede $wgHooks, because globals are EVIL. 37 * 38 * @since 1.18 39 */ 40 class Hooks { 41 /** 42 * Array of events mapped to an array of callbacks to be run 43 * when that event is triggered. 44 */ 45 protected static $handlers = array(); 46 47 /** 48 * Attach an event handler to a given hook. 49 * 50 * @param string $name Name of hook 51 * @param callable $callback Callback function to attach 52 * 53 * @since 1.18 54 */ 55 public static function register( $name, $callback ) { 56 if ( !isset( self::$handlers[$name] ) ) { 57 self::$handlers[$name] = array(); 58 } 59 60 self::$handlers[$name][] = $callback; 61 } 62 63 /** 64 * Clears hooks registered via Hooks::register(). Does not touch $wgHooks. 65 * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined. 66 * 67 * @param string $name The name of the hook to clear. 68 * 69 * @since 1.21 70 * @throws MWException If not in testing mode. 71 */ 72 public static function clear( $name ) { 73 if ( !defined( 'MW_PHPUNIT_TEST' ) ) { 74 throw new MWException( 'Cannot reset hooks in operation.' ); 75 } 76 77 unset( self::$handlers[$name] ); 78 } 79 80 /** 81 * Returns true if a hook has a function registered to it. 82 * The function may have been registered either via Hooks::register or in $wgHooks. 83 * 84 * @since 1.18 85 * 86 * @param string $name Name of hook 87 * @return bool True if the hook has a function registered to it 88 */ 89 public static function isRegistered( $name ) { 90 global $wgHooks; 91 return !empty( $wgHooks[$name] ) || !empty( self::$handlers[$name] ); 92 } 93 94 /** 95 * Returns an array of all the event functions attached to a hook 96 * This combines functions registered via Hooks::register and with $wgHooks. 97 * 98 * @since 1.18 99 * 100 * @param string $name Name of the hook 101 * @return array 102 */ 103 public static function getHandlers( $name ) { 104 global $wgHooks; 105 106 if ( !self::isRegistered( $name ) ) { 107 return array(); 108 } elseif ( !isset( self::$handlers[$name] ) ) { 109 return $wgHooks[$name]; 110 } elseif ( !isset( $wgHooks[$name] ) ) { 111 return self::$handlers[$name]; 112 } else { 113 return array_merge( self::$handlers[$name], $wgHooks[$name] ); 114 } 115 } 116 117 /** 118 * Call hook functions defined in Hooks::register and $wgHooks. 119 * 120 * For a certain hook event, fetch the array of hook events and 121 * process them. Determine the proper callback for each hook and 122 * then call the actual hook using the appropriate arguments. 123 * Finally, process the return value and return/throw accordingly. 124 * 125 * @param string $event Event name 126 * @param array $args Array of parameters passed to hook functions 127 * @param string|null $deprecatedVersion Optionally, mark hook as deprecated with version number 128 * @return bool True if no handler aborted the hook 129 * 130 * @since 1.22 A hook function is not required to return a value for 131 * processing to continue. Not returning a value (or explicitly 132 * returning null) is equivalent to returning true. 133 * @throws MWException 134 * @throws FatalError 135 */ 136 public static function run( $event, array $args = array(), $deprecatedVersion = null ) { 137 wfProfileIn( 'hook: ' . $event ); 138 foreach ( self::getHandlers( $event ) as $hook ) { 139 // Turn non-array values into an array. (Can't use casting because of objects.) 140 if ( !is_array( $hook ) ) { 141 $hook = array( $hook ); 142 } 143 144 if ( !array_filter( $hook ) ) { 145 // Either array is empty or it's an array filled with null/false/empty. 146 continue; 147 } elseif ( is_array( $hook[0] ) ) { 148 // First element is an array, meaning the developer intended 149 // the first element to be a callback. Merge it in so that 150 // processing can be uniform. 151 $hook = array_merge( $hook[0], array_slice( $hook, 1 ) ); 152 } 153 154 /** 155 * $hook can be: a function, an object, an array of $function and 156 * $data, an array of just a function, an array of object and 157 * method, or an array of object, method, and data. 158 */ 159 if ( $hook[0] instanceof Closure ) { 160 $func = "hook-$event-closure"; 161 $callback = array_shift( $hook ); 162 } elseif ( is_object( $hook[0] ) ) { 163 $object = array_shift( $hook ); 164 $method = array_shift( $hook ); 165 166 // If no method was specified, default to on$event. 167 if ( $method === null ) { 168 $method = "on$event"; 169 } 170 171 $func = get_class( $object ) . '::' . $method; 172 $callback = array( $object, $method ); 173 } elseif ( is_string( $hook[0] ) ) { 174 $func = $callback = array_shift( $hook ); 175 } else { 176 throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" ); 177 } 178 179 // Run autoloader (workaround for call_user_func_array bug) 180 // and throw error if not callable. 181 if ( !is_callable( $callback ) ) { 182 throw new MWException( 'Invalid callback in hooks for ' . $event . "\n" ); 183 } 184 185 /* 186 * Call the hook. The documentation of call_user_func_array says 187 * false is returned on failure. However, if the function signature 188 * does not match the call signature, PHP will issue an warning and 189 * return null instead. The following code catches that warning and 190 * provides better error message. 191 */ 192 $retval = null; 193 $badhookmsg = null; 194 $hook_args = array_merge( $hook, $args ); 195 196 // Profile first in case the Profiler causes errors. 197 wfProfileIn( $func ); 198 set_error_handler( 'Hooks::hookErrorHandler' ); 199 200 // mark hook as deprecated, if deprecation version is specified 201 if ( $deprecatedVersion !== null ) { 202 wfDeprecated( "$event hook (used in $func)", $deprecatedVersion ); 203 } 204 205 try { 206 $retval = call_user_func_array( $callback, $hook_args ); 207 } catch ( MWHookException $e ) { 208 $badhookmsg = $e->getMessage(); 209 } catch ( Exception $e ) { 210 restore_error_handler(); 211 throw $e; 212 } 213 restore_error_handler(); 214 wfProfileOut( $func ); 215 216 // Process the return value. 217 if ( is_string( $retval ) ) { 218 // String returned means error. 219 throw new FatalError( $retval ); 220 } elseif ( $badhookmsg !== null ) { 221 // Exception was thrown from Hooks::hookErrorHandler. 222 throw new MWException( 223 'Detected bug in an extension! ' . 224 "Hook $func has invalid call signature; " . $badhookmsg 225 ); 226 } elseif ( $retval === false ) { 227 wfProfileOut( 'hook: ' . $event ); 228 // False was returned. Stop processing, but no error. 229 return false; 230 } 231 } 232 233 wfProfileOut( 'hook: ' . $event ); 234 return true; 235 } 236 237 /** 238 * Handle PHP errors issued inside a hook. Catch errors that have to do with 239 * a function expecting a reference, and let all others pass through. 240 * 241 * This REALLY should be protected... but it's public for compatibility 242 * 243 * @since 1.18 244 * 245 * @param int $errno Error number (unused) 246 * @param string $errstr Error message 247 * @throws MWHookException If the error has to do with the function signature 248 * @return bool Always returns false 249 */ 250 public static function hookErrorHandler( $errno, $errstr ) { 251 if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) { 252 throw new MWHookException( $errstr, $errno ); 253 } 254 return false; 255 } 256 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |