Index: trunk/tools/code-utils/check-vars.php |
— | — | @@ -47,9 +47,17 @@ |
48 | 48 | var $mDebug = false; |
49 | 49 | static $mDefaultSettingsGlobals = null; |
50 | 50 | static $mRequireKnownClasses = array(); |
| 51 | + static $mRequireKnownFunctions = array(); |
51 | 52 | static $mRequireKnownConstants = array(); |
52 | 53 | |
| 54 | + static $mKnownFileClassesDefault = array(); |
| 55 | + static $mKnownFunctionsDefault = array(); |
| 56 | + static $mConstantsDefault = array(); |
| 57 | + |
53 | 58 | static $constantIgnorePrefixes = array( "PGSQL_", "OCI_", "SQLT_BLOB", "DB2_", "XMLREADER_", "SQLSRV_" ); # Ignore constants with these prefixes |
| 59 | + static $functionIgnorePrefixes = array( "pg_", "oci_", "db2_", "gmp_", "sqlsrv_", "exif_", "fss_", "tidy_", |
| 60 | + "apc_", "eaccelerator_", "xcache_", "wincache_", "apache_", "xdiff_", "wikidiff2_", "parsekit_", |
| 61 | + "wddx_", "setproctitle", "utf8_", "normalizer_" ); # Ignore functions with these prefixes |
54 | 62 | protected $generateDeprecatedList = false; |
55 | 63 | |
56 | 64 | /* Values for status */ |
— | — | @@ -154,29 +162,16 @@ |
155 | 163 | file_put_contents( $filename, $data ); |
156 | 164 | } |
157 | 165 | |
| 166 | + private function initVars() { |
| 167 | + $this->mProblemCount = 0; |
158 | 168 | |
159 | | - function load( $file, $shortcircuit = true ) { |
160 | | - $this->mProblemCount = 0; |
161 | | - $this->mFilename = $file; |
162 | | - |
163 | 169 | /* These are used even if it's shortcircuited */ |
164 | | - $this->mKnownFileClasses = array(); |
| 170 | + $this->mKnownFileClasses = self::$mKnownFileClassesDefault; |
165 | 171 | $this->mUnknownClasses = array(); |
166 | | - $this->mConstants = array(); |
| 172 | + $this->mUnknownFunctions = array(); |
| 173 | + $this->mKnownFunctions = self::$mKnownFunctionsDefault; |
| 174 | + $this->mConstants = self::$mConstantsDefault; |
167 | 175 | |
168 | | - $source = file_get_contents( $file ); |
169 | | - if ( substr( $source, 0, 3 ) == "\xEF\xBB\xBF" ) { |
170 | | - $this->warning( "$file has an UTF-8 BOM" ); |
171 | | - } |
172 | | - $source = rtrim( $source ); |
173 | | - if ( substr( $source, -2 ) == '?>' ) { |
174 | | - $this->warning( "?> at end of file is deprecated in MediaWiki code" ); |
175 | | - } |
176 | | - if ( $shortcircuit && !preg_match( "/^[^'\"#*]*function [^\"']*\$/m", $source ) ) { |
177 | | - $this->mTokens = array(); |
178 | | - return; |
179 | | - } |
180 | | - $this->mTokens = token_get_all( $source ); |
181 | 176 | $this->mStatus = self::WAITING_FUNCTION; |
182 | 177 | $this->mFunctionQualifiers = array(); |
183 | 178 | |
— | — | @@ -191,9 +186,28 @@ |
192 | 187 | 'FILEINFO_MIME', 'FILEINFO_MIME_TYPE', 'MHASH_ADLER32', |
193 | 188 | 'SIGTERM', 'SIG_DFL', |
194 | 189 | 'SVN_REVISION_HEAD', 'SVN_REVISION_INITIAL', |
195 | | - ) ; |
| 190 | + ) ; |
196 | 191 | } |
| 192 | + |
| 193 | + function load( $file, $shortcircuit = true ) { |
| 194 | + $this->initVars(); |
| 195 | + $this->mFilename = $file; |
197 | 196 | |
| 197 | + $source = file_get_contents( $file ); |
| 198 | + if ( substr( $source, 0, 3 ) == "\xEF\xBB\xBF" ) { |
| 199 | + $this->warning( "$file has an UTF-8 BOM" ); |
| 200 | + } |
| 201 | + $source = rtrim( $source ); |
| 202 | + if ( substr( $source, -2 ) == '?>' ) { |
| 203 | + $this->warning( "?> at end of file is deprecated in MediaWiki code" ); |
| 204 | + } |
| 205 | + if ( $shortcircuit && !preg_match( "/^[^'\"#*]*function [^\"']*\$/m", $source ) ) { |
| 206 | + $this->mTokens = array(); |
| 207 | + return; |
| 208 | + } |
| 209 | + $this->mTokens = token_get_all( $source ); |
| 210 | + } |
| 211 | + |
198 | 212 | static $functionQualifiers = array( T_ABSTRACT, T_PRIVATE, T_PUBLIC, T_PROTECTED, T_STATIC ); |
199 | 213 | |
200 | 214 | function execute() { |
— | — | @@ -288,6 +302,7 @@ |
289 | 303 | $this->mInSwitch = 0; |
290 | 304 | $this->mFunctionGlobals = array(); |
291 | 305 | $currentToken[0] = self::FUNCTION_DEFINITION; |
| 306 | + $this->mKnownFunctions[] = $this->mFunction; |
292 | 307 | |
293 | 308 | if ( $this->generateDeprecatedList && in_array( self::FUNCTION_DEPRECATED, $this->mFunctionQualifiers ) ) { |
294 | 309 | if ( ( substr( $this->mFunction, 0, 2 ) != "__" ) ) { |
— | — | @@ -398,6 +413,7 @@ |
399 | 414 | if ( $lastMeaningfulToken[0] == T_STRING ) { |
400 | 415 | $lastMeaningfulToken[0] = self::FUNCTION_NAME; |
401 | 416 | $this->checkDeprecation( $lastMeaningfulToken ); |
| 417 | + $this->checkFunctionName( $lastMeaningfulToken ); |
402 | 418 | } else if ( $lastMeaningfulToken[0] == self::CLASS_MEMBER ) { |
403 | 419 | $this->checkDeprecation( $lastMeaningfulToken ); |
404 | 420 | } |
— | — | @@ -411,7 +427,7 @@ |
412 | 428 | $this->checkClassName( $lastMeaningfulToken ); |
413 | 429 | } else { |
414 | 430 | |
415 | | - if ( !defined( $lastMeaningfulToken[1] ) && !in_array( $lastMeaningfulToken[1], $this->mConstants ) && !self::isIgnoreConstant( $lastMeaningfulToken[1] ) ) { |
| 431 | + if ( !defined( $lastMeaningfulToken[1] ) && !in_array( $lastMeaningfulToken[1], $this->mConstants ) && !self::inIgnoreList( $lastMeaningfulToken[1], self::$constantIgnorePrefixes ) ) { |
416 | 432 | $this->warning( "Use of undefined constant $lastMeaningfulToken[1] in line $lastMeaningfulToken[2]" ); |
417 | 433 | } |
418 | 434 | } |
— | — | @@ -480,6 +496,10 @@ |
481 | 497 | $this->mStatus = $this->mStatus - self::IN_REQUIRE_WAITING; |
482 | 498 | continue; |
483 | 499 | } |
| 500 | + if ( $requirePath == "Mail.php" ) { # PEAR mail |
| 501 | + $this->mStatus = $this->mStatus - self::IN_REQUIRE_WAITING; |
| 502 | + continue; |
| 503 | + } |
484 | 504 | |
485 | 505 | if ( ( $requirePath == '' ) || ( !file_exists( $requirePath ) && $requirePath[0] != '/' ) ) { |
486 | 506 | /* Try prepending the script folder, for maintenance scripts (but see Maintenance.php:758) */ |
— | — | @@ -491,6 +511,7 @@ |
492 | 512 | } |
493 | 513 | } else if ( isset( self::$mRequireKnownClasses[$requirePath] ) ) { |
494 | 514 | $this->mKnownFileClasses = array_merge( $this->mKnownFileClasses, self::$mRequireKnownClasses[$requirePath] ); |
| 515 | + $this->mKnownFunctions = array_merge( $this->mKnownFunctions, self::$mRequireKnownFunctions[$requirePath] ); |
495 | 516 | $this->mConstants = array_merge( $this->mConstants, self::$mRequireKnownConstants[$requirePath] ); |
496 | 517 | } else { |
497 | 518 | $newCheck = new CheckVars; |
— | — | @@ -498,8 +519,10 @@ |
499 | 520 | $newCheck->execute(); |
500 | 521 | /* Get the classes defined there */ |
501 | 522 | $this->mKnownFileClasses = array_merge( $this->mKnownFileClasses, $newCheck->mKnownFileClasses ); |
| 523 | + $this->mKnownFunctions = array_merge( $this->mKnownFunctions, $newCheck->mKnownFunctions ); |
502 | 524 | $this->mConstants = array_merge( $this->mConstants, $newCheck->mConstants ); |
503 | 525 | self::$mRequireKnownClasses[$requirePath] = $newCheck->mKnownFileClasses; |
| 526 | + self::$mRequireKnownFunctions[$requirePath] = $newCheck->mKnownFunctions; |
504 | 527 | self::$mRequireKnownConstants[$requirePath] = $newCheck->mConstants; |
505 | 528 | } |
506 | 529 | $this->mStatus = $this->mStatus - self::IN_REQUIRE_WAITING; |
— | — | @@ -562,6 +585,7 @@ |
563 | 586 | } |
564 | 587 | |
565 | 588 | $this->checkPendingClasses(); |
| 589 | + $this->checkPendingFunctions(); |
566 | 590 | } |
567 | 591 | |
568 | 592 | function checkDeprecation( $token ) { |
— | — | @@ -580,7 +604,48 @@ |
581 | 605 | } |
582 | 606 | } |
583 | 607 | } |
| 608 | + |
| 609 | + function checkFunctionName( $token, $warn = 'defer' ) { |
| 610 | + if ( !isset( $token['base'] ) ) { |
| 611 | + // Local function |
| 612 | + |
| 613 | + if ( substr( $token[1], 0, 2 ) == 'wf' ) { |
| 614 | + // MediaWiki function |
| 615 | + // TODO: List them. |
| 616 | + return; |
| 617 | + } |
| 618 | + if ( $token[1] == 'dieout' && in_array( $this->mFunction, array( 'setup_database', 'initial_setup', 'setup_plpgsql' ) ) ) { |
| 619 | + return; |
| 620 | + } |
| 621 | + |
| 622 | + if ( function_exists( $token[1] ) ) { |
| 623 | + return; |
| 624 | + } |
| 625 | + if ( in_array( $token[1], $this->mKnownFunctions ) ) { |
| 626 | + return; |
| 627 | + } |
| 628 | + |
| 629 | + if ( self::inIgnoreList( $token[1], self::$functionIgnorePrefixes ) ) { |
| 630 | + return; |
| 631 | + } |
| 632 | + |
| 633 | + if ( $warn == 'now' ) { |
| 634 | + $this->warning( "Unavailable function {$token[1]} in line {$token[2]}" ); |
| 635 | + } else if ( $warn == 'defer' ) { |
| 636 | + // Defer to the end of the file |
| 637 | + $this->mUnknownFunctions[] = $token; |
| 638 | + } |
| 639 | + |
| 640 | + } |
| 641 | + } |
584 | 642 | |
| 643 | + function checkPendingFunctions() { |
| 644 | + foreach ( $this->mUnknownFunctions as $functionToken ) { |
| 645 | + $this->checkFunctionName( $functionToken, 'now' ); |
| 646 | + } |
| 647 | + $this->mUnknownFunctions = array(); |
| 648 | + } |
| 649 | + |
585 | 650 | /* Returns a class name, or null if it couldn't guess */ |
586 | 651 | function guessClassName( $token ) { |
587 | 652 | static $wellKnownVars = array( |
— | — | @@ -736,6 +801,7 @@ |
737 | 802 | if ( substr( $token[1], 0, 12 ) == "Net_Gearman_" ) return $token[1]; # phase3/maintenance/gearman/gearman.inc |
738 | 803 | if ( $token[1] == "PEAR_Error" ) return $token[1]; # Services_JSON.php |
739 | 804 | if ( $token[1] == "PHP_Timer" ) return $token[1]; # From PEAR, used in ParserHelpers.php |
| 805 | + if ( substr( $token[1], 0, 7 ) == "Imagick" ) return $token[1]; # Imagick extension, can be used by phpunit/includes/api/RandomImageGenerator.php |
740 | 806 | |
741 | 807 | if ( !isset( $wgAutoloadLocalClasses[$token[1]] ) && !in_array( $token[1], $this->mKnownFileClasses ) ) { |
742 | 808 | if ( $warn == 'now' ) { |
— | — | @@ -757,8 +823,8 @@ |
758 | 824 | $this->mUnknownClasses = array(); |
759 | 825 | } |
760 | 826 | |
761 | | - static function isIgnoreConstant( $name ) { |
762 | | - foreach ( self::$constantIgnorePrefixes as $prefix ) { |
| 827 | + static function inIgnoreList( $name, $list ) { |
| 828 | + foreach ( $list as $prefix ) { |
763 | 829 | if ( substr( $name, 0, strlen( $prefix ) ) == $prefix ) |
764 | 830 | return true; |
765 | 831 | } |
— | — | @@ -773,6 +839,26 @@ |
774 | 840 | return "-"; |
775 | 841 | } |
776 | 842 | |
| 843 | + /** |
| 844 | + * Sets a number of files which are considered as having always been |
| 845 | + * loaded before any loaded one. Any functions/classes defined there |
| 846 | + * will be assumed to be available. |
| 847 | + */ |
| 848 | + function preloadFiles( $files ) { |
| 849 | + $this->initVars(); |
| 850 | + $this->mFilename = '__preload'; |
| 851 | + $this->mTokens = array( T_OPEN_TAG, '<?php', 0 ); |
| 852 | + |
| 853 | + for ( $i = 1; $i <= count( $files ); $i++ ) { |
| 854 | + $this->mTokens[] = array( T_REQUIRE, 'require', $i ); |
| 855 | + $this->mTokens[] = array( T_CONSTANT_ENCAPSED_STRING, "'" . $files[$i - 1] . "'", $i ); |
| 856 | + $this->mTokens[] = ';'; |
| 857 | + } |
| 858 | + $this->execute(); |
| 859 | + self::$mKnownFileClassesDefault = $this->mKnownFileClasses; |
| 860 | + self::$mKnownFunctionsDefault = $this->mKnownFunctions; |
| 861 | + self::$mConstantsDefault = $this->mConstants; |
| 862 | + } |
777 | 863 | } |
778 | 864 | |
779 | 865 | if( $argc < 2 ) { |
— | — | @@ -786,6 +872,8 @@ |
787 | 873 | $cv->setGenerateDeprecatedList( true ); |
788 | 874 | array_shift( $argv ); |
789 | 875 | } |
| 876 | +$cv->preloadFiles( array( $IP . '/includes/GlobalFunctions.php' ) ); |
| 877 | + |
790 | 878 | foreach ( $argv as $arg ) { |
791 | 879 | $cv->load( $arg ); |
792 | 880 | $cv->execute(); |