MediaWiki
REL1_19
|
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 );