r62930 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r62929‎ | r62930 | r62931 >
Date:17:29, 24 February 2010
Author:maxsem
Status:ok
Tags:
Comment:
Backported r62929 (Renamed syntaxChecker.php --> checkSyntax, our convention seems to prefer verbs. Also, fixed it to handle --modified with renamed files correctly)
Modified paths:
  • /branches/REL1_16/phase3/maintenance/checkSyntax.php (added) (history)
  • /branches/REL1_16/phase3/maintenance/syntaxChecker.php (deleted) (history)

Diff [purge]

Index: branches/REL1_16/phase3/maintenance/syntaxChecker.php
@@ -1,296 +0,0 @@
2 -<?php
3 -/**
4 - * Check syntax of all PHP files in MediaWiki
5 - *
6 - * This program is free software; you can redistribute it and/or modify
7 - * it under the terms of the GNU General Public License as published by
8 - * the Free Software Foundation; either version 2 of the License, or
9 - * (at your option) any later version.
10 - *
11 - * This program is distributed in the hope that it will be useful,
12 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 - * GNU General Public License for more details.
15 - *
16 - * You should have received a copy of the GNU General Public License along
17 - * with this program; if not, write to the Free Software Foundation, Inc.,
18 - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 - * http://www.gnu.org/copyleft/gpl.html
20 - *
21 - * @ingroup Maintenance
22 - */
23 -
24 -require_once( dirname( __FILE__ ) . '/Maintenance.php' );
25 -
26 -class SyntaxChecker extends Maintenance {
27 -
28 - // List of files we're going to check
29 - private $mFiles = array(), $mFailures = array(), $mWarnings = array();
30 - private $mIgnorePaths = array(), $mNoStyleCheckPaths = array();
31 -
32 - public function __construct() {
33 - parent::__construct();
34 - $this->mDescription = "Check syntax for all PHP files in MediaWiki";
35 - $this->addOption( 'with-extensions', 'Also recurse the extensions folder' );
36 - $this->addOption( 'path', 'Specific path (file or directory) to check, either with absolute path or relative to the root of this MediaWiki installation',
37 - false, true);
38 - $this->addOption( 'list-file', 'Text file containing list of files or directories to check', false, true);
39 - $this->addOption( 'modified', 'Check only files that were modified (requires SVN command-line client)' );
40 - $this->addOption( 'syntax-only', 'Check for syntax validity only, skip code style warnings' );
41 - }
42 -
43 - protected function getDbType() {
44 - return Maintenance::DB_NONE;
45 - }
46 -
47 - public function execute() {
48 - $this->buildFileList();
49 -
50 - // ParseKit is broken on PHP 5.3+, disabled until this is fixed
51 - $useParseKit = function_exists( 'parsekit_compile_file' ) && version_compare( PHP_VERSION, '5.3', '<' );
52 -
53 - $str = 'Checking syntax (using ' . ( $useParseKit ?
54 - 'parsekit)' : ' php -l, this can take a long time)' );
55 - $this->output( $str );
56 - foreach( $this->mFiles as $f ) {
57 - if( $useParseKit ) {
58 - $this->checkFileWithParsekit( $f );
59 - } else {
60 - $this->checkFileWithCli( $f );
61 - }
62 - if( !$this->hasOption( 'syntax-only' ) ) {
63 - $this->checkForMistakes( $f );
64 - }
65 - }
66 - $this->output( "\nDone! " . count( $this->mFiles ) . " files checked, " .
67 - count( $this->mFailures ) . " failures and " . count( $this->mWarnings ) .
68 - " warnings found\n" );
69 - }
70 -
71 - /**
72 - * Build the list of files we'll check for syntax errors
73 - */
74 - private function buildFileList() {
75 - global $IP;
76 -
77 - $this->mIgnorePaths = array(
78 - // Compat stuff, explodes on PHP 5.3
79 - "includes/NamespaceCompat.php$",
80 - "DiscussionThreading/REV",
81 - );
82 -
83 - $this->mNoStyleCheckPaths = array(
84 - // Third-party code we don't care about
85 - "/activemq_stomp/",
86 - "EmailPage/phpMailer",
87 - "FCKeditor/fckeditor/",
88 - '\bphplot-',
89 - "/svggraph/",
90 - "\bjsmin.php$",
91 - "OggHandler/PEAR/",
92 - "QPoll/Excel/",
93 - "/geshi/",
94 - "/smarty/",
95 - );
96 -
97 - if ( $this->hasOption( 'path' ) ) {
98 - $path = $this->getOption( 'path' );
99 - if ( !$this->addPath( $path ) ) {
100 - $this->error( "Error: can't find file or directory $path\n", true );
101 - }
102 - return; // process only this path
103 - } elseif ( $this->hasOption( 'list-file' ) ) {
104 - $file = $this->getOption( 'list-file' );
105 - $f = @fopen( $file, 'r' );
106 - if ( !$f ) {
107 - $this->error( "Can't open file $file\n", true );
108 - }
109 - while( $path = trim( fgets( $f ) ) ) {
110 - $this->addPath( $path );
111 - }
112 - fclose( $f );
113 - return;
114 - } elseif ( $this->hasOption( 'modified' ) ) {
115 - $this->output( "Retrieving list from Subversion... " );
116 - $parentDir = wfEscapeShellArg( dirname( __FILE__ ) . '/..' );
117 - $output = wfShellExec( "svn status --ignore-externals $parentDir", $retval );
118 - if ( $retval ) {
119 - $this->error( "Error retrieving list from Subversion!\n", true );
120 - } else {
121 - $this->output( "done\n" );
122 - }
123 -
124 - preg_match_all( '/^\s*[AM]\s+(.*?)\r?$/m', $output, $matches );
125 - foreach ( $matches[1] as $file ) {
126 - if ( self::isSuitableFile( $file ) && !is_dir( $file ) ) {
127 - $this->mFiles[] = $file;
128 - }
129 - }
130 - return;
131 - }
132 -
133 - $this->output( 'Building file list...', 'listfiles' );
134 -
135 - // Only check files in these directories.
136 - // Don't just put $IP, because the recursive dir thingie goes into all subdirs
137 - $dirs = array(
138 - $IP . '/includes',
139 - $IP . '/config',
140 - $IP . '/languages',
141 - $IP . '/maintenance',
142 - $IP . '/skins',
143 - );
144 - if( $this->hasOption( 'with-extensions' ) ) {
145 - $dirs[] = $IP . '/extensions';
146 - }
147 -
148 - foreach( $dirs as $d ) {
149 - $this->addDirectoryContent( $d );
150 - }
151 -
152 - // Manually add two user-editable files that are usually sources of problems
153 - if ( file_exists( "$IP/LocalSettings.php" ) ) {
154 - $this->mFiles[] = "$IP/LocalSettings.php";
155 - }
156 - if ( file_exists( "$IP/AdminSettings.php" ) ) {
157 - $this->mFiles[] = "$IP/AdminSettings.php";
158 - }
159 -
160 - $this->output( 'done.', 'listfiles' );
161 - }
162 -
163 - /**
164 - * Returns true if $file is of a type we can check
165 - */
166 - private function isSuitableFile( $file ) {
167 - $ext = pathinfo( $file, PATHINFO_EXTENSION );
168 - if ( $ext != 'php' && $ext != 'inc' && $ext != 'php5' )
169 - return false;
170 - foreach( $this->mIgnorePaths as $regex ) {
171 - $m = array();
172 - if ( preg_match( "~{$regex}~", $file, $m ) )
173 - return false;
174 - }
175 - return true;
176 - }
177 -
178 - /**
179 - * Add given path to file list, searching it in include path if needed
180 - */
181 - private function addPath( $path ) {
182 - global $IP;
183 - return $this->addFileOrDir( $path ) || $this->addFileOrDir( "$IP/$path" );
184 - }
185 -
186 - /**
187 - * Add given file to file list, or, if it's a directory, add its content
188 - */
189 - private function addFileOrDir( $path ) {
190 - if ( is_dir( $path ) ) {
191 - $this->addDirectoryContent( $path );
192 - } elseif ( file_exists( $path ) ) {
193 - $this->mFiles[] = $path;
194 - } else {
195 - return false;
196 - }
197 - return true;
198 - }
199 -
200 - /**
201 - * Add all suitable files in given directory or its subdirectories to the file list
202 - *
203 - * @param $dir String: directory to process
204 - */
205 - private function addDirectoryContent( $dir ) {
206 - $iterator = new RecursiveIteratorIterator(
207 - new RecursiveDirectoryIterator( $dir ),
208 - RecursiveIteratorIterator::SELF_FIRST
209 - );
210 - foreach ( $iterator as $file ) {
211 - if ( $this->isSuitableFile( $file->getRealPath() ) ) {
212 - $this->mFiles[] = $file->getRealPath();
213 - }
214 - }
215 - }
216 -
217 - /**
218 - * Check a file for syntax errors using Parsekit. Shamelessly stolen
219 - * from tools/lint.php by TimStarling
220 - * @param $file String Path to a file to check for syntax errors
221 - * @return boolean
222 - */
223 - private function checkFileWithParsekit( $file ) {
224 - static $okErrors = array(
225 - 'Redefining already defined constructor',
226 - 'Assigning the return value of new by reference is deprecated',
227 - );
228 - $errors = array();
229 - parsekit_compile_file( $file, $errors, PARSEKIT_SIMPLE );
230 - $ret = true;
231 - if ( $errors ) {
232 - foreach ( $errors as $error ) {
233 - foreach ( $okErrors as $okError ) {
234 - if ( substr( $error['errstr'], 0, strlen( $okError ) ) == $okError ) {
235 - continue 2;
236 - }
237 - }
238 - $ret = false;
239 - $this->output( "Error in $file line {$error['lineno']}: {$error['errstr']}\n" );
240 - $this->mFailures[$file] = $errors;
241 - }
242 - }
243 - return $ret;
244 - }
245 -
246 - /**
247 - * Check a file for syntax errors using php -l
248 - * @param $file String Path to a file to check for syntax errors
249 - * @return boolean
250 - */
251 - private function checkFileWithCli( $file ) {
252 - $res = exec( 'php -l ' . wfEscapeShellArg( $file ) );
253 - if( strpos( $res, 'No syntax errors detected' ) === false ) {
254 - $this->mFailures[$file] = $res;
255 - $this->output( $res . "\n" );
256 - return false;
257 - }
258 - return true;
259 - }
260 -
261 - /**
262 - * Check a file for non-fatal coding errors, such as byte-order marks in the beginning
263 - * or pointless ?> closing tags at the end.
264 - *
265 - * @param $file String String Path to a file to check for errors
266 - * @return boolean
267 - */
268 - private function checkForMistakes( $file ) {
269 - foreach( $this->mNoStyleCheckPaths as $regex ) {
270 - $m = array();
271 - if ( preg_match( "~{$regex}~", $file, $m ) )
272 - return;
273 - }
274 -
275 - $text = file_get_contents( $file );
276 -
277 - $this->checkRegex( $file, $text, '/^[\s\r\n]+<\?/', 'leading whitespace' );
278 - $this->checkRegex( $file, $text, '/\?>[\s\r\n]*$/', 'trailing ?>' );
279 - $this->checkRegex( $file, $text, '/^[\xFF\xFE\xEF]/', 'byte-order mark' );
280 - }
281 -
282 - private function checkRegex( $file, $text, $regex, $desc ) {
283 - if ( !preg_match( $regex, $text ) ) {
284 - return;
285 - }
286 -
287 - if ( !isset( $this->mWarnings[$file] ) ) {
288 - $this->mWarnings[$file] = array();
289 - }
290 - $this->mWarnings[$file][] = $desc;
291 - $this->output( "Warning in file $file: $desc found.\n" );
292 - }
293 -}
294 -
295 -$maintClass = "SyntaxChecker";
296 -require_once( DO_MAINTENANCE );
297 -
Index: branches/REL1_16/phase3/maintenance/checkSyntax.php
@@ -0,0 +1,296 @@
 2+<?php
 3+/**
 4+ * Check syntax of all PHP files in MediaWiki
 5+ *
 6+ * This program is free software; you can redistribute it and/or modify
 7+ * it under the terms of the GNU General Public License as published by
 8+ * the Free Software Foundation; either version 2 of the License, or
 9+ * (at your option) any later version.
 10+ *
 11+ * This program is distributed in the hope that it will be useful,
 12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ * GNU General Public License for more details.
 15+ *
 16+ * You should have received a copy of the GNU General Public License along
 17+ * with this program; if not, write to the Free Software Foundation, Inc.,
 18+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 19+ * http://www.gnu.org/copyleft/gpl.html
 20+ *
 21+ * @ingroup Maintenance
 22+ */
 23+
 24+require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 25+
 26+class CheckSyntax extends Maintenance {
 27+
 28+ // List of files we're going to check
 29+ private $mFiles = array(), $mFailures = array(), $mWarnings = array();
 30+ private $mIgnorePaths = array(), $mNoStyleCheckPaths = array();
 31+
 32+ public function __construct() {
 33+ parent::__construct();
 34+ $this->mDescription = "Check syntax for all PHP files in MediaWiki";
 35+ $this->addOption( 'with-extensions', 'Also recurse the extensions folder' );
 36+ $this->addOption( 'path', 'Specific path (file or directory) to check, either with absolute path or relative to the root of this MediaWiki installation',
 37+ false, true);
 38+ $this->addOption( 'list-file', 'Text file containing list of files or directories to check', false, true);
 39+ $this->addOption( 'modified', 'Check only files that were modified (requires SVN command-line client)' );
 40+ $this->addOption( 'syntax-only', 'Check for syntax validity only, skip code style warnings' );
 41+ }
 42+
 43+ protected function getDbType() {
 44+ return Maintenance::DB_NONE;
 45+ }
 46+
 47+ public function execute() {
 48+ $this->buildFileList();
 49+
 50+ // ParseKit is broken on PHP 5.3+, disabled until this is fixed
 51+ $useParseKit = function_exists( 'parsekit_compile_file' ) && version_compare( PHP_VERSION, '5.3', '<' );
 52+
 53+ $str = 'Checking syntax (using ' . ( $useParseKit ?
 54+ 'parsekit)' : ' php -l, this can take a long time)' );
 55+ $this->output( $str );
 56+ foreach( $this->mFiles as $f ) {
 57+ if( $useParseKit ) {
 58+ $this->checkFileWithParsekit( $f );
 59+ } else {
 60+ $this->checkFileWithCli( $f );
 61+ }
 62+ if( !$this->hasOption( 'syntax-only' ) ) {
 63+ $this->checkForMistakes( $f );
 64+ }
 65+ }
 66+ $this->output( "\nDone! " . count( $this->mFiles ) . " files checked, " .
 67+ count( $this->mFailures ) . " failures and " . count( $this->mWarnings ) .
 68+ " warnings found\n" );
 69+ }
 70+
 71+ /**
 72+ * Build the list of files we'll check for syntax errors
 73+ */
 74+ private function buildFileList() {
 75+ global $IP;
 76+
 77+ $this->mIgnorePaths = array(
 78+ // Compat stuff, explodes on PHP 5.3
 79+ "includes/NamespaceCompat.php$",
 80+ "DiscussionThreading/REV",
 81+ );
 82+
 83+ $this->mNoStyleCheckPaths = array(
 84+ // Third-party code we don't care about
 85+ "/activemq_stomp/",
 86+ "EmailPage/phpMailer",
 87+ "FCKeditor/fckeditor/",
 88+ '\bphplot-',
 89+ "/svggraph/",
 90+ "\bjsmin.php$",
 91+ "OggHandler/PEAR/",
 92+ "QPoll/Excel/",
 93+ "/geshi/",
 94+ "/smarty/",
 95+ );
 96+
 97+ if ( $this->hasOption( 'path' ) ) {
 98+ $path = $this->getOption( 'path' );
 99+ if ( !$this->addPath( $path ) ) {
 100+ $this->error( "Error: can't find file or directory $path\n", true );
 101+ }
 102+ return; // process only this path
 103+ } elseif ( $this->hasOption( 'list-file' ) ) {
 104+ $file = $this->getOption( 'list-file' );
 105+ $f = @fopen( $file, 'r' );
 106+ if ( !$f ) {
 107+ $this->error( "Can't open file $file\n", true );
 108+ }
 109+ while( $path = trim( fgets( $f ) ) ) {
 110+ $this->addPath( $path );
 111+ }
 112+ fclose( $f );
 113+ return;
 114+ } elseif ( $this->hasOption( 'modified' ) ) {
 115+ $this->output( "Retrieving list from Subversion... " );
 116+ $parentDir = wfEscapeShellArg( dirname( __FILE__ ) . '/..' );
 117+ $output = wfShellExec( "svn status --ignore-externals $parentDir", $retval );
 118+ if ( $retval ) {
 119+ $this->error( "Error retrieving list from Subversion!\n", true );
 120+ } else {
 121+ $this->output( "done\n" );
 122+ }
 123+
 124+ preg_match_all( '/^\s*[AM].{7}(.*?)\r?$/m', $output, $matches );
 125+ foreach ( $matches[1] as $file ) {
 126+ if ( self::isSuitableFile( $file ) && !is_dir( $file ) ) {
 127+ $this->mFiles[] = $file;
 128+ }
 129+ }
 130+ return;
 131+ }
 132+
 133+ $this->output( 'Building file list...', 'listfiles' );
 134+
 135+ // Only check files in these directories.
 136+ // Don't just put $IP, because the recursive dir thingie goes into all subdirs
 137+ $dirs = array(
 138+ $IP . '/includes',
 139+ $IP . '/config',
 140+ $IP . '/languages',
 141+ $IP . '/maintenance',
 142+ $IP . '/skins',
 143+ );
 144+ if( $this->hasOption( 'with-extensions' ) ) {
 145+ $dirs[] = $IP . '/extensions';
 146+ }
 147+
 148+ foreach( $dirs as $d ) {
 149+ $this->addDirectoryContent( $d );
 150+ }
 151+
 152+ // Manually add two user-editable files that are usually sources of problems
 153+ if ( file_exists( "$IP/LocalSettings.php" ) ) {
 154+ $this->mFiles[] = "$IP/LocalSettings.php";
 155+ }
 156+ if ( file_exists( "$IP/AdminSettings.php" ) ) {
 157+ $this->mFiles[] = "$IP/AdminSettings.php";
 158+ }
 159+
 160+ $this->output( 'done.', 'listfiles' );
 161+ }
 162+
 163+ /**
 164+ * Returns true if $file is of a type we can check
 165+ */
 166+ private function isSuitableFile( $file ) {
 167+ $ext = pathinfo( $file, PATHINFO_EXTENSION );
 168+ if ( $ext != 'php' && $ext != 'inc' && $ext != 'php5' )
 169+ return false;
 170+ foreach( $this->mIgnorePaths as $regex ) {
 171+ $m = array();
 172+ if ( preg_match( "~{$regex}~", $file, $m ) )
 173+ return false;
 174+ }
 175+ return true;
 176+ }
 177+
 178+ /**
 179+ * Add given path to file list, searching it in include path if needed
 180+ */
 181+ private function addPath( $path ) {
 182+ global $IP;
 183+ return $this->addFileOrDir( $path ) || $this->addFileOrDir( "$IP/$path" );
 184+ }
 185+
 186+ /**
 187+ * Add given file to file list, or, if it's a directory, add its content
 188+ */
 189+ private function addFileOrDir( $path ) {
 190+ if ( is_dir( $path ) ) {
 191+ $this->addDirectoryContent( $path );
 192+ } elseif ( file_exists( $path ) ) {
 193+ $this->mFiles[] = $path;
 194+ } else {
 195+ return false;
 196+ }
 197+ return true;
 198+ }
 199+
 200+ /**
 201+ * Add all suitable files in given directory or its subdirectories to the file list
 202+ *
 203+ * @param $dir String: directory to process
 204+ */
 205+ private function addDirectoryContent( $dir ) {
 206+ $iterator = new RecursiveIteratorIterator(
 207+ new RecursiveDirectoryIterator( $dir ),
 208+ RecursiveIteratorIterator::SELF_FIRST
 209+ );
 210+ foreach ( $iterator as $file ) {
 211+ if ( $this->isSuitableFile( $file->getRealPath() ) ) {
 212+ $this->mFiles[] = $file->getRealPath();
 213+ }
 214+ }
 215+ }
 216+
 217+ /**
 218+ * Check a file for syntax errors using Parsekit. Shamelessly stolen
 219+ * from tools/lint.php by TimStarling
 220+ * @param $file String Path to a file to check for syntax errors
 221+ * @return boolean
 222+ */
 223+ private function checkFileWithParsekit( $file ) {
 224+ static $okErrors = array(
 225+ 'Redefining already defined constructor',
 226+ 'Assigning the return value of new by reference is deprecated',
 227+ );
 228+ $errors = array();
 229+ parsekit_compile_file( $file, $errors, PARSEKIT_SIMPLE );
 230+ $ret = true;
 231+ if ( $errors ) {
 232+ foreach ( $errors as $error ) {
 233+ foreach ( $okErrors as $okError ) {
 234+ if ( substr( $error['errstr'], 0, strlen( $okError ) ) == $okError ) {
 235+ continue 2;
 236+ }
 237+ }
 238+ $ret = false;
 239+ $this->output( "Error in $file line {$error['lineno']}: {$error['errstr']}\n" );
 240+ $this->mFailures[$file] = $errors;
 241+ }
 242+ }
 243+ return $ret;
 244+ }
 245+
 246+ /**
 247+ * Check a file for syntax errors using php -l
 248+ * @param $file String Path to a file to check for syntax errors
 249+ * @return boolean
 250+ */
 251+ private function checkFileWithCli( $file ) {
 252+ $res = exec( 'php -l ' . wfEscapeShellArg( $file ) );
 253+ if( strpos( $res, 'No syntax errors detected' ) === false ) {
 254+ $this->mFailures[$file] = $res;
 255+ $this->output( $res . "\n" );
 256+ return false;
 257+ }
 258+ return true;
 259+ }
 260+
 261+ /**
 262+ * Check a file for non-fatal coding errors, such as byte-order marks in the beginning
 263+ * or pointless ?> closing tags at the end.
 264+ *
 265+ * @param $file String String Path to a file to check for errors
 266+ * @return boolean
 267+ */
 268+ private function checkForMistakes( $file ) {
 269+ foreach( $this->mNoStyleCheckPaths as $regex ) {
 270+ $m = array();
 271+ if ( preg_match( "~{$regex}~", $file, $m ) )
 272+ return;
 273+ }
 274+
 275+ $text = file_get_contents( $file );
 276+
 277+ $this->checkRegex( $file, $text, '/^[\s\r\n]+<\?/', 'leading whitespace' );
 278+ $this->checkRegex( $file, $text, '/\?>[\s\r\n]*$/', 'trailing ?>' );
 279+ $this->checkRegex( $file, $text, '/^[\xFF\xFE\xEF]/', 'byte-order mark' );
 280+ }
 281+
 282+ private function checkRegex( $file, $text, $regex, $desc ) {
 283+ if ( !preg_match( $regex, $text ) ) {
 284+ return;
 285+ }
 286+
 287+ if ( !isset( $this->mWarnings[$file] ) ) {
 288+ $this->mWarnings[$file] = array();
 289+ }
 290+ $this->mWarnings[$file][] = $desc;
 291+ $this->output( "Warning in file $file: $desc found.\n" );
 292+ }
 293+}
 294+
 295+$maintClass = "CheckSyntax";
 296+require_once( DO_MAINTENANCE );
 297+
Property changes on: branches/REL1_16/phase3/maintenance/checkSyntax.php
___________________________________________________________________
Name: svn:eol-style
1298 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r62931Updated history for r62930maxsem17:42, 24 February 2010

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r62929Renamed syntaxChecker.php --> checkSyntax, our convention seems to prefer ver...maxsem17:21, 24 February 2010

Status & tagging log