[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Simple script that try to find documented hook and hooks actually 4 * in the code and show what's missing. 5 * 6 * This script assumes that: 7 * - hooks names in hooks.txt are at the beginning of a line and single quoted. 8 * - hooks names in code are the first parameter of wfRunHooks. 9 * 10 * if --online option is passed, the script will compare the hooks in the code 11 * with the ones at http://www.mediawiki.org/wiki/Manual:Hooks 12 * 13 * Any instance of wfRunHooks that doesn't meet these parameters will be noted. 14 * 15 * Copyright © Antoine Musso 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License as published by 19 * the Free Software Foundation; either version 2 of the License, or 20 * (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License along 28 * with this program; if not, write to the Free Software Foundation, Inc., 29 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 30 * http://www.gnu.org/copyleft/gpl.html 31 * 32 * @file 33 * @ingroup Maintenance 34 * @author Antoine Musso <hashar at free dot fr> 35 */ 36 37 require_once __DIR__ . '/Maintenance.php'; 38 39 /** 40 * Maintenance script that compares documented and actually present mismatches. 41 * 42 * @ingroup Maintenance 43 */ 44 class FindHooks extends Maintenance { 45 /* 46 * Hooks that are ignored 47 */ 48 protected static $ignore = array( 'testRunLegacyHooks' ); 49 50 public function __construct() { 51 parent::__construct(); 52 $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong'; 53 $this->addOption( 'online', 'Check against MediaWiki.org hook documentation' ); 54 } 55 56 public function getDbType() { 57 return Maintenance::DB_NONE; 58 } 59 60 public function execute() { 61 global $IP; 62 63 $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' ); 64 $potential = array(); 65 $bad = array(); 66 67 // TODO: Don't hardcode the list of directories 68 $pathinc = array( 69 $IP . '/', 70 $IP . '/includes/', 71 $IP . '/includes/actions/', 72 $IP . '/includes/api/', 73 $IP . '/includes/cache/', 74 $IP . '/includes/changes/', 75 $IP . '/includes/clientpool/', 76 $IP . '/includes/content/', 77 $IP . '/includes/context/', 78 $IP . '/includes/dao/', 79 $IP . '/includes/db/', 80 $IP . '/includes/debug/', 81 $IP . '/includes/deferred/', 82 $IP . '/includes/diff/', 83 $IP . '/includes/externalstore/', 84 $IP . '/includes/filebackend/', 85 $IP . '/includes/filerepo/', 86 $IP . '/includes/filerepo/file/', 87 $IP . '/includes/gallery/', 88 $IP . '/includes/htmlform/', 89 $IP . '/includes/installer/', 90 $IP . '/includes/interwiki/', 91 $IP . '/includes/jobqueue/', 92 $IP . '/includes/json/', 93 $IP . '/includes/logging/', 94 $IP . '/includes/media/', 95 $IP . '/includes/page/', 96 $IP . '/includes/parser/', 97 $IP . '/includes/rcfeed/', 98 $IP . '/includes/resourceloader/', 99 $IP . '/includes/revisiondelete/', 100 $IP . '/includes/search/', 101 $IP . '/includes/site/', 102 $IP . '/includes/skins/', 103 $IP . '/includes/specialpage/', 104 $IP . '/includes/specials/', 105 $IP . '/includes/upload/', 106 $IP . '/includes/utils/', 107 $IP . '/languages/', 108 $IP . '/maintenance/', 109 $IP . '/maintenance/language/', 110 $IP . '/tests/', 111 $IP . '/tests/parser/', 112 $IP . '/tests/phpunit/suites/', 113 ); 114 115 foreach ( $pathinc as $dir ) { 116 $potential = array_merge( $potential, $this->getHooksFromPath( $dir ) ); 117 $bad = array_merge( $bad, $this->getBadHooksFromPath( $dir ) ); 118 } 119 120 $potential = array_unique( $potential ); 121 $bad = array_unique( $bad ); 122 $todo = array_diff( $potential, $documented ); 123 $deprecated = array_diff( $documented, $potential ); 124 125 // let's show the results: 126 $this->printArray( 'Undocumented', $todo ); 127 $this->printArray( 'Documented and not found', $deprecated ); 128 $this->printArray( 'Unclear hook calls', $bad ); 129 130 if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) { 131 $this->output( "Looks good!\n" ); 132 } 133 } 134 135 /** 136 * Get the hook documentation, either locally or from MediaWiki.org 137 * @param string $doc 138 * @return array Array of documented hooks 139 */ 140 private function getHooksFromDoc( $doc ) { 141 if ( $this->hasOption( 'online' ) ) { 142 return $this->getHooksFromOnlineDoc(); 143 } else { 144 return $this->getHooksFromLocalDoc( $doc ); 145 } 146 } 147 148 /** 149 * Get hooks from a local file (for example docs/hooks.txt) 150 * @param string $doc Filename to look in 151 * @return array Array of documented hooks 152 */ 153 private function getHooksFromLocalDoc( $doc ) { 154 $m = array(); 155 $content = file_get_contents( $doc ); 156 preg_match_all( "/\n'(.*?)':/", $content, $m ); 157 158 return array_unique( $m[1] ); 159 } 160 161 /** 162 * Get hooks from www.mediawiki.org using the API 163 * @return array Array of documented hooks 164 */ 165 private function getHooksFromOnlineDoc() { 166 // All hooks 167 $allhookdata = Http::get( 168 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' 169 . 'cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' 170 ); 171 $allhookdata = unserialize( $allhookdata ); 172 $allhooks = array(); 173 foreach ( $allhookdata['query']['categorymembers'] as $page ) { 174 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); 175 if ( $found ) { 176 $hook = str_replace( ' ', '_', $matches[1] ); 177 $allhooks[] = $hook; 178 } 179 } 180 // Removed hooks 181 $oldhookdata = Http::get( 182 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' 183 . 'cmtitle=Category:Removed_hooks&cmlimit=500&format=php' 184 ); 185 $oldhookdata = unserialize( $oldhookdata ); 186 $removed = array(); 187 foreach ( $oldhookdata['query']['categorymembers'] as $page ) { 188 $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); 189 if ( $found ) { 190 $hook = str_replace( ' ', '_', $matches[1] ); 191 $removed[] = $hook; 192 } 193 } 194 195 return array_diff( $allhooks, $removed ); 196 } 197 198 /** 199 * Get hooks from a PHP file 200 * @param string $file Full filename to the PHP file. 201 * @return array Array of hooks found 202 */ 203 private function getHooksFromFile( $file ) { 204 $content = file_get_contents( $file ); 205 $m = array(); 206 preg_match_all( 207 '/(?:wfRunHooks|Hooks\:\:run|ContentHandler\:\:runLegacyHooks)\(\s*([\'"])(.*?)\1/', 208 $content, 209 $m 210 ); 211 212 return $m[2]; 213 } 214 215 /** 216 * Get hooks from the source code. 217 * @param string $path Directory where the include files can be found 218 * @return array Array of hooks found 219 */ 220 private function getHooksFromPath( $path ) { 221 $hooks = array(); 222 $dh = opendir( $path ); 223 if ( $dh ) { 224 while ( ( $file = readdir( $dh ) ) !== false ) { 225 if ( filetype( $path . $file ) == 'file' ) { 226 $hooks = array_merge( $hooks, $this->getHooksFromFile( $path . $file ) ); 227 } 228 } 229 closedir( $dh ); 230 } 231 232 return $hooks; 233 } 234 235 /** 236 * Get bad hooks (where the hook name could not be determined) from a PHP file 237 * @param string $file Full filename to the PHP file. 238 * @return array Array of bad wfRunHooks() lines 239 */ 240 private function getBadHooksFromFile( $file ) { 241 $content = file_get_contents( $file ); 242 $m = array(); 243 # We want to skip the "function wfRunHooks()" one. :) 244 preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m ); 245 $list = array(); 246 foreach ( $m[0] as $match ) { 247 $list[] = $match . "(" . $file . ")"; 248 } 249 250 return $list; 251 } 252 253 /** 254 * Get bad hooks from the source code. 255 * @param string $path Directory where the include files can be found 256 * @return array Array of bad wfRunHooks() lines 257 */ 258 private function getBadHooksFromPath( $path ) { 259 $hooks = array(); 260 $dh = opendir( $path ); 261 if ( $dh ) { 262 while ( ( $file = readdir( $dh ) ) !== false ) { 263 # We don't want to read this file as it contains bad calls to wfRunHooks() 264 if ( filetype( $path . $file ) == 'file' && !$path . $file == __FILE__ ) { 265 $hooks = array_merge( $hooks, $this->getBadHooksFromFile( $path . $file ) ); 266 } 267 } 268 closedir( $dh ); 269 } 270 271 return $hooks; 272 } 273 274 /** 275 * Nicely output the array 276 * @param string $msg A message to show before the value 277 * @param array $arr 278 * @param bool $sort Whether to sort the array (Default: true) 279 */ 280 private function printArray( $msg, $arr, $sort = true ) { 281 if ( $sort ) { 282 asort( $arr ); 283 } 284 285 foreach ( $arr as $v ) { 286 if ( !in_array( $v, self::$ignore ) ) { 287 $this->output( "$msg: $v\n" ); 288 } 289 } 290 } 291 } 292 293 $maintClass = 'FindHooks'; 294 require_once RUN_MAINTENANCE_IF_MAIN;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |