MediaWiki  REL1_21
Hooks.php
Go to the documentation of this file.
00001 <?php
00029 class MWHookException extends MWException {}
00030 
00038 class Hooks {
00039 
00040         protected static $handlers = array();
00041 
00052         public static function clear( $name ) {
00053                 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
00054                         throw new MWException( 'can not reset hooks in operation.' );
00055                 }
00056 
00057                 unset( self::$handlers[$name] );
00058         }
00059 
00068         public static function register( $name, $callback ) {
00069                 if( !isset( self::$handlers[$name] ) ) {
00070                         self::$handlers[$name] = array();
00071                 }
00072 
00073                 self::$handlers[$name][] = $callback;
00074         }
00075 
00085         public static function isRegistered( $name ) {
00086                 global $wgHooks;
00087 
00088                 return !empty( $wgHooks[$name] ) || !empty( self::$handlers[$name] );
00089         }
00090 
00102         public static function getHandlers( $name ) {
00103                 global $wgHooks;
00104 
00105                 // Return quickly in the most common case
00106                 if ( empty( self::$handlers[$name] ) && empty( $wgHooks[$name] ) ) {
00107                         return array();
00108                 }
00109 
00110                 if ( !is_array( self::$handlers ) ) {
00111                         throw new MWException( "Local hooks array is not an array!\n" );
00112                 }
00113 
00114                 if ( !is_array( $wgHooks ) ) {
00115                         throw new MWException( "Global hooks array is not an array!\n" );
00116                 }
00117 
00118                 if ( empty( Hooks::$handlers[$name] ) ) {
00119                         $hooks = $wgHooks[$name];
00120                 } elseif ( empty( $wgHooks[$name] ) ) {
00121                         $hooks = Hooks::$handlers[$name];
00122                 } else {
00123                         // so they are both not empty...
00124                         $hooks = array_merge( Hooks::$handlers[$name], $wgHooks[$name] );
00125                 }
00126 
00127                 if ( !is_array( $hooks ) ) {
00128                         throw new MWException( "Hooks array for event '$name' is not an array!\n" );
00129                 }
00130 
00131                 return $hooks;
00132         }
00133 
00144         public static function run( $event, $args = array() ) {
00145                 global $wgHooks;
00146 
00147                 // Return quickly in the most common case
00148                 if ( empty( self::$handlers[$event] ) && empty( $wgHooks[$event] ) ) {
00149                         return true;
00150                 }
00151 
00152                 wfProfileIn( 'hook: ' . $event );
00153                 $hooks = self::getHandlers( $event );
00154 
00155                 foreach ( $hooks as $hook ) {
00156                         $object = null;
00157                         $method = null;
00158                         $func = null;
00159                         $data = null;
00160                         $have_data = false;
00161                         $closure = false;
00162                         $badhookmsg = false;
00163 
00169                         if ( is_array( $hook ) ) {
00170                                 if ( count( $hook ) < 1 ) {
00171                                         throw new MWException( 'Empty array in hooks for ' . $event . "\n" );
00172                                 } elseif ( is_object( $hook[0] ) ) {
00173                                         $object = $hook[0];
00174                                         if ( $object instanceof Closure ) {
00175                                                 $closure = true;
00176                                                 if ( count( $hook ) > 1 ) {
00177                                                         $data = $hook[1];
00178                                                         $have_data = true;
00179                                                 }
00180                                         } else {
00181                                                 if ( count( $hook ) < 2 ) {
00182                                                         $method = 'on' . $event;
00183                                                 } else {
00184                                                         $method = $hook[1];
00185                                                         if ( count( $hook ) > 2 ) {
00186                                                                 $data = $hook[2];
00187                                                                 $have_data = true;
00188                                                         }
00189                                                 }
00190                                         }
00191                                 } elseif ( is_string( $hook[0] ) ) {
00192                                         $func = $hook[0];
00193                                         if ( count( $hook ) > 1) {
00194                                                 $data = $hook[1];
00195                                                 $have_data = true;
00196                                         }
00197                                 } else {
00198                                         throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" );
00199                                 }
00200                         } elseif ( is_string( $hook ) ) { # functions look like strings, too
00201                                 $func = $hook;
00202                         } elseif ( is_object( $hook ) ) {
00203                                 $object = $hook;
00204                                 if ( $object instanceof Closure ) {
00205                                         $closure = true;
00206                                 } else {
00207                                         $method = "on" . $event;
00208                                 }
00209                         } else {
00210                                 throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" );
00211                         }
00212 
00213                         /* We put the first data element on, if needed. */
00214                         if ( $have_data ) {
00215                                 $hook_args = array_merge( array( $data ), $args );
00216                         } else {
00217                                 $hook_args = $args;
00218                         }
00219 
00220                         if ( $closure ) {
00221                                 $callback = $object;
00222                                 $func = "hook-$event-closure";
00223                         } elseif ( isset( $object ) ) {
00224                                 $func = get_class( $object ) . '::' . $method;
00225                                 $callback = array( $object, $method );
00226                         } else {
00227                                 $callback = $func;
00228                         }
00229 
00230                         // Run autoloader (workaround for call_user_func_array bug)
00231                         is_callable( $callback );
00232 
00251                         $retval = null;
00252                         set_error_handler( 'Hooks::hookErrorHandler' );
00253                         wfProfileIn( $func );
00254                         try {
00255                                 $retval = call_user_func_array( $callback, $hook_args );
00256                         } catch ( MWHookException $e ) {
00257                                 $badhookmsg = $e->getMessage();
00258                         }
00259                         wfProfileOut( $func );
00260                         restore_error_handler();
00261 
00262                         /* String return is an error; false return means stop processing. */
00263                         if ( is_string( $retval ) ) {
00264                                 throw new FatalError( $retval );
00265                         } elseif( $retval === null ) {
00266                                 if ( $closure ) {
00267                                         $prettyFunc = "$event closure";
00268                                 } elseif( is_array( $callback ) ) {
00269                                         if( is_object( $callback[0] ) ) {
00270                                                 $prettyClass = get_class( $callback[0] );
00271                                         } else {
00272                                                 $prettyClass = strval( $callback[0] );
00273                                         }
00274                                         $prettyFunc = $prettyClass . '::' . strval( $callback[1] );
00275                                 } else {
00276                                         $prettyFunc = strval( $callback );
00277                                 }
00278                                 if ( $badhookmsg ) {
00279                                         throw new MWException(
00280                                                 'Detected bug in an extension! ' .
00281                                                 "Hook $prettyFunc has invalid call signature; " . $badhookmsg
00282                                         );
00283                                 } else {
00284                                         throw new MWException(
00285                                                 'Detected bug in an extension! ' .
00286                                                 "Hook $prettyFunc failed to return a value; " .
00287                                                 'should return true to continue hook processing or false to abort.'
00288                                         );
00289                                 }
00290                         } elseif ( !$retval ) {
00291                                 wfProfileOut( 'hook: ' . $event );
00292                                 return false;
00293                         }
00294                 }
00295 
00296                 wfProfileOut( 'hook: ' . $event );
00297                 return true;
00298         }
00299 
00310         public static function hookErrorHandler( $errno, $errstr ) {
00311                 if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) {
00312                         throw new MWHookException( $errstr );
00313                 }
00314                 return false;
00315         }
00316 }