MediaWiki  REL1_19
Hooks.php
Go to the documentation of this file.
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 }