PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/wwwroot/mediawiki/maintenance/findHooks.php

https://github.com/spring/spring-website
PHP | 273 lines | 167 code | 20 blank | 86 comment | 22 complexity | 952f715241ce8755a61dd31dfd70731a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, LGPL-3.0, BSD-3-Clause
  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. require_once __DIR__ . '/Maintenance.php';
  37. /**
  38. * Maintenance script that compares documented and actually present mismatches.
  39. *
  40. * @ingroup Maintenance
  41. */
  42. class FindHooks extends Maintenance {
  43. /*
  44. * Hooks that are ignored
  45. */
  46. protected static $ignore = array( 'testRunLegacyHooks' );
  47. public function __construct() {
  48. parent::__construct();
  49. $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong';
  50. $this->addOption( 'online', 'Check against MediaWiki.org hook documentation' );
  51. }
  52. public function getDbType() {
  53. return Maintenance::DB_NONE;
  54. }
  55. public function execute() {
  56. global $IP;
  57. $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' );
  58. $potential = array();
  59. $bad = array();
  60. $pathinc = array(
  61. $IP . '/',
  62. $IP . '/includes/',
  63. $IP . '/includes/actions/',
  64. $IP . '/includes/api/',
  65. $IP . '/includes/cache/',
  66. $IP . '/includes/changes/',
  67. $IP . '/includes/clientpool/',
  68. $IP . '/includes/content/',
  69. $IP . '/includes/context/',
  70. $IP . '/includes/dao/',
  71. $IP . '/includes/db/',
  72. $IP . '/includes/debug/',
  73. $IP . '/includes/deferred/',
  74. $IP . '/includes/diff/',
  75. $IP . '/includes/externalstore/',
  76. $IP . '/includes/filebackend/',
  77. $IP . '/includes/filerepo/',
  78. $IP . '/includes/filerepo/file/',
  79. $IP . '/includes/gallery/',
  80. $IP . '/includes/htmlform/',
  81. $IP . '/includes/installer/',
  82. $IP . '/includes/interwiki/',
  83. $IP . '/includes/jobqueue/',
  84. $IP . '/includes/json/',
  85. $IP . '/includes/logging/',
  86. $IP . '/includes/media/',
  87. $IP . '/includes/parser/',
  88. $IP . '/includes/rcfeed/',
  89. $IP . '/includes/resourceloader/',
  90. $IP . '/includes/revisiondelete/',
  91. $IP . '/includes/search/',
  92. $IP . '/includes/site/',
  93. $IP . '/includes/specialpage/',
  94. $IP . '/includes/specials/',
  95. $IP . '/includes/upload/',
  96. $IP . '/languages/',
  97. $IP . '/maintenance/',
  98. $IP . '/maintenance/language/',
  99. $IP . '/tests/',
  100. $IP . '/tests/parser/',
  101. $IP . '/tests/phpunit/suites/',
  102. $IP . '/skins/',
  103. );
  104. foreach ( $pathinc as $dir ) {
  105. $potential = array_merge( $potential, $this->getHooksFromPath( $dir ) );
  106. $bad = array_merge( $bad, $this->getBadHooksFromPath( $dir ) );
  107. }
  108. $potential = array_unique( $potential );
  109. $bad = array_unique( $bad );
  110. $todo = array_diff( $potential, $documented );
  111. $deprecated = array_diff( $documented, $potential );
  112. // let's show the results:
  113. $this->printArray( 'Undocumented', $todo );
  114. $this->printArray( 'Documented and not found', $deprecated );
  115. $this->printArray( 'Unclear hook calls', $bad );
  116. if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) {
  117. $this->output( "Looks good!\n" );
  118. }
  119. }
  120. /**
  121. * Get the hook documentation, either locally or from MediaWiki.org
  122. * @return array of documented hooks
  123. */
  124. private function getHooksFromDoc( $doc ) {
  125. if ( $this->hasOption( 'online' ) ) {
  126. return $this->getHooksFromOnlineDoc();
  127. } else {
  128. return $this->getHooksFromLocalDoc( $doc );
  129. }
  130. }
  131. /**
  132. * Get hooks from a local file (for example docs/hooks.txt)
  133. * @param $doc string: filename to look in
  134. * @return array of documented hooks
  135. */
  136. private function getHooksFromLocalDoc( $doc ) {
  137. $m = array();
  138. $content = file_get_contents( $doc );
  139. preg_match_all( "/\n'(.*?)':/", $content, $m );
  140. return array_unique( $m[1] );
  141. }
  142. /**
  143. * Get hooks from www.mediawiki.org using the API
  144. * @return array of documented hooks
  145. */
  146. private function getHooksFromOnlineDoc() {
  147. // All hooks
  148. $allhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' );
  149. $allhookdata = unserialize( $allhookdata );
  150. $allhooks = array();
  151. foreach ( $allhookdata['query']['categorymembers'] as $page ) {
  152. $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
  153. if ( $found ) {
  154. $hook = str_replace( ' ', '_', $matches[1] );
  155. $allhooks[] = $hook;
  156. }
  157. }
  158. // Removed hooks
  159. $oldhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Removed_hooks&cmlimit=500&format=php' );
  160. $oldhookdata = unserialize( $oldhookdata );
  161. $removed = array();
  162. foreach ( $oldhookdata['query']['categorymembers'] as $page ) {
  163. $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
  164. if ( $found ) {
  165. $hook = str_replace( ' ', '_', $matches[1] );
  166. $removed[] = $hook;
  167. }
  168. }
  169. return array_diff( $allhooks, $removed );
  170. }
  171. /**
  172. * Get hooks from a PHP file
  173. * @param $file string Full filename to the PHP file.
  174. * @return array of hooks found.
  175. */
  176. private function getHooksFromFile( $file ) {
  177. $content = file_get_contents( $file );
  178. $m = array();
  179. preg_match_all( '/(?:wfRunHooks|Hooks\:\:run|ContentHandler\:\:runLegacyHooks)\(\s*([\'"])(.*?)\1/', $content, $m );
  180. return $m[2];
  181. }
  182. /**
  183. * Get hooks from the source code.
  184. * @param $path Directory where the include files can be found
  185. * @return array of hooks found.
  186. */
  187. private function getHooksFromPath( $path ) {
  188. $hooks = array();
  189. $dh = opendir( $path );
  190. if ( $dh ) {
  191. while ( ( $file = readdir( $dh ) ) !== false ) {
  192. if ( filetype( $path . $file ) == 'file' ) {
  193. $hooks = array_merge( $hooks, $this->getHooksFromFile( $path . $file ) );
  194. }
  195. }
  196. closedir( $dh );
  197. }
  198. return $hooks;
  199. }
  200. /**
  201. * Get bad hooks (where the hook name could not be determined) from a PHP file
  202. * @param $file string Full filename to the PHP file.
  203. * @return array of bad wfRunHooks() lines
  204. */
  205. private function getBadHooksFromFile( $file ) {
  206. $content = file_get_contents( $file );
  207. $m = array();
  208. # We want to skip the "function wfRunHooks()" one. :)
  209. preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m );
  210. $list = array();
  211. foreach ( $m[0] as $match ) {
  212. $list[] = $match . "(" . $file . ")";
  213. }
  214. return $list;
  215. }
  216. /**
  217. * Get bad hooks from the source code.
  218. * @param $path Directory where the include files can be found
  219. * @return array of bad wfRunHooks() lines
  220. */
  221. private function getBadHooksFromPath( $path ) {
  222. $hooks = array();
  223. $dh = opendir( $path );
  224. if ( $dh ) {
  225. while ( ( $file = readdir( $dh ) ) !== false ) {
  226. # We don't want to read this file as it contains bad calls to wfRunHooks()
  227. if ( filetype( $path . $file ) == 'file' && !$path . $file == __FILE__ ) {
  228. $hooks = array_merge( $hooks, $this->getBadHooksFromFile( $path . $file ) );
  229. }
  230. }
  231. closedir( $dh );
  232. }
  233. return $hooks;
  234. }
  235. /**
  236. * Nicely output the array
  237. * @param $msg String: a message to show before the value
  238. * @param $arr Array: an array
  239. * @param $sort Boolean: whether to sort the array (Default: true)
  240. */
  241. private function printArray( $msg, $arr, $sort = true ) {
  242. if ( $sort ) {
  243. asort( $arr );
  244. }
  245. foreach ( $arr as $v ) {
  246. if ( !in_array( $v, self::$ignore ) ) {
  247. $this->output( "$msg: $v\n" );
  248. }
  249. }
  250. }
  251. }
  252. $maintClass = 'FindHooks';
  253. require_once RUN_MAINTENANCE_IF_MAIN;