MediaWiki  REL1_19
findHooks.php
Go to the documentation of this file.
00001 <?php
00037 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
00038 
00039 class FindHooks extends Maintenance {
00040         public function __construct() {
00041                 parent::__construct();
00042                 $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong';
00043                 $this->addOption( 'online', 'Check against MediaWiki.org hook documentation' );
00044         }
00045 
00046         public function getDbType() {
00047                 return Maintenance::DB_NONE;
00048         }
00049 
00050         public function execute() {
00051                 global $IP;
00052 
00053                 $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' );
00054                 $potential = array();
00055                 $bad = array();
00056                 $pathinc = array(
00057                         $IP . '/',
00058                         $IP . '/includes/',
00059                         $IP . '/includes/actions/',
00060                         $IP . '/includes/api/',
00061                         $IP . '/includes/cache/',
00062                         $IP . '/includes/context/',
00063                         $IP . '/includes/db/',
00064                         $IP . '/includes/diff/',
00065                         $IP . '/includes/filerepo/',
00066                         $IP . '/includes/installer/',
00067                         $IP . '/includes/interwiki/',
00068                         $IP . '/includes/media/',
00069                         $IP . '/includes/parser/',
00070                         $IP . '/includes/resourceloader/',
00071                         $IP . '/includes/revisiondelete/',
00072                         $IP . '/includes/search/',
00073                         $IP . '/includes/specials/',
00074                         $IP . '/includes/upload/',
00075                         $IP . '/languages/',
00076                         $IP . '/maintenance/',
00077                         $IP . '/tests/',
00078                         $IP . '/tests/parser/',
00079                         $IP . '/tests/phpunit/suites/',
00080                         $IP . '/skins/',
00081                 );
00082 
00083                 foreach ( $pathinc as $dir ) {
00084                         $potential = array_merge( $potential, $this->getHooksFromPath( $dir ) );
00085                         $bad = array_merge( $bad, $this->getBadHooksFromPath( $dir ) );
00086                 }
00087 
00088                 $potential = array_unique( $potential );
00089                 $bad = array_unique( $bad );
00090                 $todo = array_diff( $potential, $documented );
00091                 $deprecated = array_diff( $documented, $potential );
00092 
00093                 // let's show the results:
00094                 $this->printArray( 'Undocumented', $todo );
00095                 $this->printArray( 'Documented and not found', $deprecated );
00096                 $this->printArray( 'Unclear hook calls', $bad );
00097 
00098                 if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 )
00099                 {
00100                         $this->output( "Looks good!\n" );
00101                 }
00102         }
00103 
00108         private function getHooksFromDoc( $doc ) {
00109                 if ( $this->hasOption( 'online' ) ) {
00110                         return $this->getHooksFromOnlineDoc( );
00111                 } else {
00112                         return $this->getHooksFromLocalDoc( $doc );
00113                 }
00114         }
00115 
00121         private function getHooksFromLocalDoc( $doc ) {
00122                         $m = array();
00123                         $content = file_get_contents( $doc );
00124                         preg_match_all( "/\n'(.*?)'/", $content, $m );
00125                         return array_unique( $m[1] );
00126         }
00127 
00132         private function getHooksFromOnlineDoc( ) {
00133                         // All hooks
00134                         $allhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' );
00135                         $allhookdata = unserialize( $allhookdata );
00136                         $allhooks = array();
00137                         foreach ( $allhookdata['query']['categorymembers'] as $page ) {
00138                                 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
00139                                 if ( $found ) {
00140                                         $hook = str_replace( ' ', '_', $matches[1] );
00141                                         $allhooks[] = $hook;
00142                                 }
00143                         }
00144                         // Removed hooks
00145                         $oldhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Removed_hooks&cmlimit=500&format=php' );
00146                         $oldhookdata = unserialize( $oldhookdata );
00147                         $removed = array();
00148                         foreach ( $oldhookdata['query']['categorymembers'] as $page ) {
00149                                 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
00150                                 if ( $found ) {
00151                                         $hook = str_replace( ' ', '_', $matches[1] );
00152                                         $removed[] = $hook;
00153                                 }
00154                         }
00155                         return array_diff( $allhooks, $removed );
00156         }
00157 
00163         private function getHooksFromFile( $file ) {
00164                 $content = file_get_contents( $file );
00165                 $m = array();
00166                 preg_match_all( '/(?:wfRunHooks|Hooks\:\:run)\(\s*([\'"])(.*?)\1/', $content, $m );
00167                 return $m[2];
00168         }
00169 
00175         private function getHooksFromPath( $path ) {
00176                 $hooks = array();
00177                 $dh = opendir( $path );
00178                 if ( $dh ) {
00179                         while ( ( $file = readdir( $dh ) ) !== false ) {
00180                                 if ( filetype( $path . $file ) == 'file' ) {
00181                                         $hooks = array_merge( $hooks, $this->getHooksFromFile( $path . $file ) );
00182                                 }
00183                         }
00184                         closedir( $dh );
00185                 }
00186                 return $hooks;
00187         }
00188 
00194         private function getBadHooksFromFile( $file ) {
00195                 $content = file_get_contents( $file );
00196                 $m = array();
00197                 # We want to skip the "function wfRunHooks()" one.  :)
00198                 preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m );
00199                 $list = array();
00200                 foreach ( $m[0] as $match ) {
00201                         $list[] = $match . "(" . $file . ")";
00202                 }
00203                 return $list;
00204         }
00205 
00211         private function getBadHooksFromPath( $path ) {
00212                 $hooks = array();
00213                 $dh = opendir( $path );
00214                 if ( $dh ) {
00215                         while ( ( $file = readdir( $dh ) ) !== false ) {
00216                                 # We don't want to read this file as it contains bad calls to wfRunHooks()
00217                                 if ( filetype( $path . $file ) == 'file' && !$path . $file == __FILE__ ) {
00218                                         $hooks = array_merge( $hooks, $this->getBadHooksFromFile( $path . $file ) );
00219                                 }
00220                         }
00221                         closedir( $dh );
00222                 }
00223                 return $hooks;
00224         }
00225 
00232         private function printArray( $msg, $arr, $sort = true ) {
00233                 if ( $sort ) {
00234                         asort( $arr );
00235                 }
00236                 foreach ( $arr as $v ) {
00237                         $this->output( "$msg: $v\n" );
00238                 }
00239         }
00240 }
00241 
00242 $maintClass = 'FindHooks';
00243 require_once( RUN_MAINTENANCE_IF_MAIN );