MediaWiki
REL1_19
|
00001 <?php 00026 class MWHookException extends MWException {} 00027 00033 class Hooks { 00034 00035 protected static $handlers = array(); 00036 00044 public static function register( $name, $callback ) { 00045 if( !isset( self::$handlers[$name] ) ) { 00046 self::$handlers[$name] = array(); 00047 } 00048 00049 self::$handlers[$name][] = $callback; 00050 } 00051 00058 public static function isRegistered( $name ) { 00059 if( !isset( self::$handlers[$name] ) ) { 00060 self::$handlers[$name] = array(); 00061 } 00062 00063 return ( count( self::$handlers[$name] ) != 0 ); 00064 } 00065 00072 public static function getHandlers( $name ) { 00073 if( !isset( self::$handlers[$name] ) ) { 00074 return array(); 00075 } 00076 00077 return self::$handlers[$name]; 00078 } 00079 00091 public static function run( $event, $args = array() ) { 00092 global $wgHooks; 00093 00094 // Return quickly in the most common case 00095 if ( !isset( self::$handlers[$event] ) && !isset( $wgHooks[$event] ) ) { 00096 return true; 00097 } 00098 00099 if ( !is_array( self::$handlers ) ) { 00100 throw new MWException( "Local hooks array is not an array!\n" ); 00101 } 00102 00103 if ( !is_array( $wgHooks ) ) { 00104 throw new MWException( "Global hooks array is not an array!\n" ); 00105 } 00106 00107 $new_handlers = (array) self::$handlers; 00108 $old_handlers = (array) $wgHooks; 00109 00110 $hook_array = array_merge( $new_handlers, $old_handlers ); 00111 00112 if ( !is_array( $hook_array[$event] ) ) { 00113 throw new MWException( "Hooks array for event '$event' is not an array!\n" ); 00114 } 00115 00116 foreach ( $hook_array[$event] as $index => $hook ) { 00117 $object = null; 00118 $method = null; 00119 $func = null; 00120 $data = null; 00121 $have_data = false; 00122 $closure = false; 00123 $badhookmsg = false; 00124 00130 if ( is_array( $hook ) ) { 00131 if ( count( $hook ) < 1 ) { 00132 throw new MWException( 'Empty array in hooks for ' . $event . "\n" ); 00133 } elseif ( is_object( $hook[0] ) ) { 00134 $object = $hook_array[$event][$index][0]; 00135 if ( $object instanceof Closure ) { 00136 $closure = true; 00137 if ( count( $hook ) > 1 ) { 00138 $data = $hook[1]; 00139 $have_data = true; 00140 } 00141 } else { 00142 if ( count( $hook ) < 2 ) { 00143 $method = 'on' . $event; 00144 } else { 00145 $method = $hook[1]; 00146 if ( count( $hook ) > 2 ) { 00147 $data = $hook[2]; 00148 $have_data = true; 00149 } 00150 } 00151 } 00152 } elseif ( is_string( $hook[0] ) ) { 00153 $func = $hook[0]; 00154 if ( count( $hook ) > 1) { 00155 $data = $hook[1]; 00156 $have_data = true; 00157 } 00158 } else { 00159 throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" ); 00160 } 00161 } elseif ( is_string( $hook ) ) { # functions look like strings, too 00162 $func = $hook; 00163 } elseif ( is_object( $hook ) ) { 00164 $object = $hook_array[$event][$index]; 00165 if ( $object instanceof Closure ) { 00166 $closure = true; 00167 } else { 00168 $method = "on" . $event; 00169 } 00170 } else { 00171 throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" ); 00172 } 00173 00174 /* We put the first data element on, if needed. */ 00175 if ( $have_data ) { 00176 $hook_args = array_merge( array( $data ), $args ); 00177 } else { 00178 $hook_args = $args; 00179 } 00180 00181 if ( $closure ) { 00182 $callback = $object; 00183 $func = "hook-$event-closure"; 00184 } elseif ( isset( $object ) ) { 00185 $func = get_class( $object ) . '::' . $method; 00186 $callback = array( $object, $method ); 00187 } else { 00188 $callback = $func; 00189 } 00190 00191 // Run autoloader (workaround for call_user_func_array bug) 00192 is_callable( $callback ); 00193 00212 $retval = null; 00213 set_error_handler( 'Hooks::hookErrorHandler' ); 00214 wfProfileIn( $func ); 00215 try { 00216 $retval = call_user_func_array( $callback, $hook_args ); 00217 } catch ( MWHookException $e ) { 00218 $badhookmsg = $e->getMessage(); 00219 } 00220 wfProfileOut( $func ); 00221 restore_error_handler(); 00222 00223 /* String return is an error; false return means stop processing. */ 00224 if ( is_string( $retval ) ) { 00225 throw new FatalError( $retval ); 00226 } elseif( $retval === null ) { 00227 if ( $closure ) { 00228 $prettyFunc = "$event closure"; 00229 } elseif( is_array( $callback ) ) { 00230 if( is_object( $callback[0] ) ) { 00231 $prettyClass = get_class( $callback[0] ); 00232 } else { 00233 $prettyClass = strval( $callback[0] ); 00234 } 00235 $prettyFunc = $prettyClass . '::' . strval( $callback[1] ); 00236 } else { 00237 $prettyFunc = strval( $callback ); 00238 } 00239 if ( $badhookmsg ) { 00240 throw new MWException( 00241 'Detected bug in an extension! ' . 00242 "Hook $prettyFunc has invalid call signature; " . $badhookmsg 00243 ); 00244 } else { 00245 throw new MWException( 00246 'Detected bug in an extension! ' . 00247 "Hook $prettyFunc failed to return a value; " . 00248 'should return true to continue hook processing or false to abort.' 00249 ); 00250 } 00251 } elseif ( !$retval ) { 00252 return false; 00253 } 00254 } 00255 00256 return true; 00257 } 00258 00266 public static function hookErrorHandler( $errno, $errstr ) { 00267 if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) { 00268 throw new MWHookException( $errstr ); 00269 } 00270 return false; 00271 } 00272 }