MediaWiki
REL1_23
|
00001 <?php 00037 require_once __DIR__ . '/Maintenance.php'; 00038 00044 class FindHooks extends Maintenance { 00045 /* 00046 * Hooks that are ignored 00047 */ 00048 protected static $ignore = array( 'testRunLegacyHooks' ); 00049 00050 public function __construct() { 00051 parent::__construct(); 00052 $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong'; 00053 $this->addOption( 'online', 'Check against MediaWiki.org hook documentation' ); 00054 } 00055 00056 public function getDbType() { 00057 return Maintenance::DB_NONE; 00058 } 00059 00060 public function execute() { 00061 global $IP; 00062 00063 $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' ); 00064 $potential = array(); 00065 $bad = array(); 00066 $pathinc = array( 00067 $IP . '/', 00068 $IP . '/includes/', 00069 $IP . '/includes/actions/', 00070 $IP . '/includes/api/', 00071 $IP . '/includes/cache/', 00072 $IP . '/includes/changes/', 00073 $IP . '/includes/clientpool/', 00074 $IP . '/includes/content/', 00075 $IP . '/includes/context/', 00076 $IP . '/includes/dao/', 00077 $IP . '/includes/db/', 00078 $IP . '/includes/debug/', 00079 $IP . '/includes/deferred/', 00080 $IP . '/includes/diff/', 00081 $IP . '/includes/externalstore/', 00082 $IP . '/includes/filebackend/', 00083 $IP . '/includes/filerepo/', 00084 $IP . '/includes/filerepo/file/', 00085 $IP . '/includes/gallery/', 00086 $IP . '/includes/htmlform/', 00087 $IP . '/includes/installer/', 00088 $IP . '/includes/interwiki/', 00089 $IP . '/includes/jobqueue/', 00090 $IP . '/includes/json/', 00091 $IP . '/includes/logging/', 00092 $IP . '/includes/media/', 00093 $IP . '/includes/parser/', 00094 $IP . '/includes/rcfeed/', 00095 $IP . '/includes/resourceloader/', 00096 $IP . '/includes/revisiondelete/', 00097 $IP . '/includes/search/', 00098 $IP . '/includes/site/', 00099 $IP . '/includes/specialpage/', 00100 $IP . '/includes/specials/', 00101 $IP . '/includes/upload/', 00102 $IP . '/languages/', 00103 $IP . '/maintenance/', 00104 $IP . '/maintenance/language/', 00105 $IP . '/tests/', 00106 $IP . '/tests/parser/', 00107 $IP . '/tests/phpunit/suites/', 00108 $IP . '/skins/', 00109 ); 00110 00111 foreach ( $pathinc as $dir ) { 00112 $potential = array_merge( $potential, $this->getHooksFromPath( $dir ) ); 00113 $bad = array_merge( $bad, $this->getBadHooksFromPath( $dir ) ); 00114 } 00115 00116 $potential = array_unique( $potential ); 00117 $bad = array_unique( $bad ); 00118 $todo = array_diff( $potential, $documented ); 00119 $deprecated = array_diff( $documented, $potential ); 00120 00121 // let's show the results: 00122 $this->printArray( 'Undocumented', $todo ); 00123 $this->printArray( 'Documented and not found', $deprecated ); 00124 $this->printArray( 'Unclear hook calls', $bad ); 00125 00126 if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) { 00127 $this->output( "Looks good!\n" ); 00128 } 00129 } 00130 00135 private function getHooksFromDoc( $doc ) { 00136 if ( $this->hasOption( 'online' ) ) { 00137 return $this->getHooksFromOnlineDoc(); 00138 } else { 00139 return $this->getHooksFromLocalDoc( $doc ); 00140 } 00141 } 00142 00148 private function getHooksFromLocalDoc( $doc ) { 00149 $m = array(); 00150 $content = file_get_contents( $doc ); 00151 preg_match_all( "/\n'(.*?)':/", $content, $m ); 00152 return array_unique( $m[1] ); 00153 } 00154 00159 private function getHooksFromOnlineDoc() { 00160 // All hooks 00161 $allhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' ); 00162 $allhookdata = unserialize( $allhookdata ); 00163 $allhooks = array(); 00164 foreach ( $allhookdata['query']['categorymembers'] as $page ) { 00165 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); 00166 if ( $found ) { 00167 $hook = str_replace( ' ', '_', $matches[1] ); 00168 $allhooks[] = $hook; 00169 } 00170 } 00171 // Removed hooks 00172 $oldhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Removed_hooks&cmlimit=500&format=php' ); 00173 $oldhookdata = unserialize( $oldhookdata ); 00174 $removed = array(); 00175 foreach ( $oldhookdata['query']['categorymembers'] as $page ) { 00176 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); 00177 if ( $found ) { 00178 $hook = str_replace( ' ', '_', $matches[1] ); 00179 $removed[] = $hook; 00180 } 00181 } 00182 return array_diff( $allhooks, $removed ); 00183 } 00184 00190 private function getHooksFromFile( $file ) { 00191 $content = file_get_contents( $file ); 00192 $m = array(); 00193 preg_match_all( '/(?:wfRunHooks|Hooks\:\:run|ContentHandler\:\:runLegacyHooks)\(\s*([\'"])(.*?)\1/', $content, $m ); 00194 return $m[2]; 00195 } 00196 00202 private function getHooksFromPath( $path ) { 00203 $hooks = array(); 00204 $dh = opendir( $path ); 00205 if ( $dh ) { 00206 while ( ( $file = readdir( $dh ) ) !== false ) { 00207 if ( filetype( $path . $file ) == 'file' ) { 00208 $hooks = array_merge( $hooks, $this->getHooksFromFile( $path . $file ) ); 00209 } 00210 } 00211 closedir( $dh ); 00212 } 00213 return $hooks; 00214 } 00215 00221 private function getBadHooksFromFile( $file ) { 00222 $content = file_get_contents( $file ); 00223 $m = array(); 00224 # We want to skip the "function wfRunHooks()" one. :) 00225 preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m ); 00226 $list = array(); 00227 foreach ( $m[0] as $match ) { 00228 $list[] = $match . "(" . $file . ")"; 00229 } 00230 return $list; 00231 } 00232 00238 private function getBadHooksFromPath( $path ) { 00239 $hooks = array(); 00240 $dh = opendir( $path ); 00241 if ( $dh ) { 00242 while ( ( $file = readdir( $dh ) ) !== false ) { 00243 # We don't want to read this file as it contains bad calls to wfRunHooks() 00244 if ( filetype( $path . $file ) == 'file' && !$path . $file == __FILE__ ) { 00245 $hooks = array_merge( $hooks, $this->getBadHooksFromFile( $path . $file ) ); 00246 } 00247 } 00248 closedir( $dh ); 00249 } 00250 return $hooks; 00251 } 00252 00259 private function printArray( $msg, $arr, $sort = true ) { 00260 if ( $sort ) { 00261 asort( $arr ); 00262 } 00263 00264 foreach ( $arr as $v ) { 00265 if ( !in_array( $v, self::$ignore ) ) { 00266 $this->output( "$msg: $v\n" ); 00267 } 00268 } 00269 } 00270 } 00271 00272 $maintClass = 'FindHooks'; 00273 require_once RUN_MAINTENANCE_IF_MAIN;