Skip to content

Commit 8e762f2

Browse files
author
Yuriy Nasretdinov
committed
Added check for class_exists (and interface_exists) for 'extends' and 'implements'
1 parent 185138c commit 8e762f2

File tree

1 file changed

+124
-102
lines changed

1 file changed

+124
-102
lines changed

PHP/CodeCoverage/Util.php

Lines changed: 124 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ public static function getLinesToBeIgnored($filename, $cacheTokens = TRUE)
245245
* @param $filename string Filename to be checked
246246
* @param $toplevel_funcs array List of function calls at top level that are allowed (e.g. array('define'))
247247
* @param $classes array Classes that are present in file with respect of namespaces
248+
* @param $errmsg string Error message will be written to this variable if false is returned
248249
*
249250
* @return bool
250251
*/
@@ -260,131 +261,152 @@ public static function canIncludeFile($filename, array $toplevel_funcs, &$classe
260261
$state = "default";
261262
$depth = 0; // depth of "(" or "{"
262263
$line = 1;
263-
$namespace = "";
264+
$namespace = "\\";
265+
$classname = "";
264266

265267
foreach ($tokens as $row) {
266-
// printf("%-12s ", $state);
267268
if (is_array($row)) {
268269
list($token, $text, $line) = $row;
269270
$text = str_replace("\n", '\\n', $text);
270-
// echo token_name($token) . " '$text' on line $line\n";
271-
if ($token === T_WHITESPACE || $token === T_COMMENT || $token === T_DOC_COMMENT) continue;
271+
} else {
272+
$token = $text = $row;
273+
}
274+
275+
if ($token === T_WHITESPACE || $token === T_COMMENT || $token === T_DOC_COMMENT) continue;
276+
277+
// printf("%-12s ", $state);
278+
279+
switch ($state) {
280+
case "funccall":
281+
if ($token === "(") $depth++;
282+
else if ($token === ")") $depth--;
283+
if ($depth == 0) $state = "funccall_end";
284+
break;
272285

273-
switch ($state) {
274-
case "default":
275-
if ($token !== T_OPEN_TAG) {
276-
$errmsg = "Have something before <?php tag on line $line";
286+
case "default":
287+
if ($token !== T_OPEN_TAG) {
288+
$errmsg = "Have something before <?php tag on line $line";
289+
return false;
290+
}
291+
$state = "root";
292+
break;
293+
294+
case "root":
295+
if ($token === T_STRING) { // function call
296+
if (!isset($toplevel_funcs[$text])) {
297+
$errmsg = "Forbidden top level function call: $text(...) on line $line";
277298
return false;
278299
}
300+
$state = "funccall";
301+
$depth = 0;
302+
} else if ($token === T_ABSTRACT || $token === T_FINAL) {
303+
continue;
304+
} else if ($token === T_CLASS) {
305+
$state = "classdef";
306+
$classname = "";
307+
} else if ($token === T_CLOSE_TAG) {
308+
$state = "default";
309+
} else if ($token === T_NAMESPACE) {
310+
$state = "namespace";
311+
$namespace = "";
312+
} else if ($token === T_USE) {
313+
$state = "use";
314+
} else if ($token === T_FUNCTION) {
315+
$state = "funcdef";
316+
} else {
317+
$errmsg = "Disallowed top level token '$text' on line $line";
318+
return false;
319+
}
320+
break;
321+
322+
case "use":
323+
if ($row === ";") $state = "root";
324+
break;
325+
326+
case "namespace":
327+
if ($token === ";") {
279328
$state = "root";
280-
break;
281-
case "root":
282-
if ($token === T_STRING) { // function call
283-
if (!isset($toplevel_funcs[$text])) {
284-
$errmsg = "Forbidden top level function call: $text(...) on line $line";
285-
return false;
286-
}
287-
$state = "funccall";
288-
$depth = 0;
289-
} else if ($token === T_ABSTRACT || $token === T_FINAL) {
290-
continue;
291-
} else if ($token === T_CLASS) {
292-
$state = "classdef";
293-
} else if ($token === T_CLOSE_TAG) {
294-
$state = "default";
295-
} else if ($token === T_NAMESPACE) {
296-
$state = "namespace";
297-
$namespace = "";
298-
} else if ($token === T_USE) {
299-
$state = "use";
300-
} else if ($token === T_FUNCTION) {
301-
$state = "funcdef";
302-
} else {
303-
$errmsg = "Disallowed top level token " . token_name($token) . " ($text) on line $line";
329+
} else if ($token === T_NS_SEPARATOR || $token === T_STRING) {
330+
$namespace .= $text;
331+
} else {
332+
$errmsg = "Unexpected token '$row' on line $line (expected ';')";
333+
return false;
334+
}
335+
break;
336+
337+
case "classdef":
338+
case "extends":
339+
case "implements":
340+
if ($token === T_EXTENDS || $token === T_IMPLEMENTS || $token === "{") {
341+
if (!$classname) {
342+
$errmsg = "Empty classname on line $line";
304343
return false;
305344
}
306-
break;
307-
case "namespace":
308-
$namespace .= $text;
309-
break;
310-
case "classdef":
311-
if ($token === T_EXTENDS) {
312-
$state = "extends";
313-
} else if ($token === T_IMPLEMENTS) {
314-
$state = "implements";
315-
} else if ($token === T_STRING) {
316-
$classname = $namespace . "\\" . $text;
345+
346+
if ($classname[0] != '\\') {
347+
$classname = rtrim($namespace, "\\") . "\\" . $classname;
348+
}
349+
350+
if ($state == "classdef") {
317351
if (class_exists($classname)) {
318352
$errmsg = "Class '$classname' already exists on line $line";
319353
return false;
320-
} else {
321-
$classes[] = $classname;
322354
}
323-
} else {
324-
$errmsg = "Unexpected token " . token_name($token) . " ($text) on line $line";
325-
return false;
355+
$classes[] = $classname;
326356
}
327-
break;
328-
case "funccall_end":
329-
$errmsg = "Unexpected terminator for function call: " . token_name($token) . " ($text) on line $line";
330-
return false;
331-
}
332357

333-
} else {
334-
// echo "$row\n";
335-
switch ($state) {
336-
case "funccall":
337-
if ($row === "(") $depth++;
338-
else if ($row === ")") $depth--;
339-
if ($depth == 0) $state = "funccall_end";
340-
break;
341-
case "funccall_end":
342-
if ($row !== ";") {
343-
$errmsg = "Unexpected terminator for function call: '$row' on line $line";
344-
return false;
345-
}
346-
$state = "root";
347-
break;
348-
case "namespace":
349-
if ($row === ";") {
350-
$state = "root";
351-
} else {
352-
$errmsg = "Unexpected token '$row' on line $line (expected ';')";
353-
return false;
358+
if ($state != "classdef") {
359+
$func = ($state == "extends" ? "class_exists" : "interface_exists");
360+
if (!$func($classname, true)) {
361+
$errmsg = "Class '$classname' does not exist on line $line";
362+
return false;
363+
}
354364
}
355-
break;
356-
case "use":
357-
if ($row === ";") $state = "root";
358-
break;
359-
case "classdef":
360-
case "extends":
361-
case "implements":
362-
if ($row === "{") {
365+
366+
if ($token === "{") {
363367
$state = "class";
364368
$depth = 1;
365369
} else {
366-
$errmsg = "Unexpected token '$row' on line $line (expected '{')";
367-
return false;
368-
}
369-
break;
370-
case "funcdef":
371-
if ($row === "{") {
372-
$state = "function";
373-
$depth = 1;
370+
$state = ($token == T_EXTENDS ? "extends" : "implements");
371+
$classname = "";
374372
}
375-
break;
376-
case "function":
377-
if ($row === "{") $depth++;
378-
else if ($row === "}") $depth--;
379-
if ($depth == 0) $state = "root";
380-
break;
381-
case "class":
382-
if ($row === "{") $depth++;
383-
else if ($row === "}") $depth--;
384-
if ($depth == 0) $state = "root";
385-
break;
386-
}
373+
} else if ($token === T_STRING || $token == T_NS_SEPARATOR) {
374+
$classname .= $text;
375+
} else {
376+
$errmsg = "Unexpected token '$text' on line $line (expected '{')";
377+
return false;
378+
}
379+
break;
380+
381+
case "funccall_end":
382+
if ($token !== ";") {
383+
$errmsg = "Unexpected terminator for function call: '$text' on line $line";
384+
return false;
385+
}
386+
$state = "root";
387+
break;
388+
389+
case "funcdef":
390+
if ($row === "{") {
391+
$state = "function";
392+
$depth = 1;
393+
}
394+
break;
395+
396+
case "function":
397+
if ($token === "{") $depth++;
398+
else if ($token === "}") $depth--;
399+
if ($depth == 0) $state = "root";
400+
break;
401+
402+
case "class":
403+
if ($token === "{") $depth++;
404+
else if ($token === "}") $depth--;
405+
if ($depth == 0) $state = "root";
406+
break;
387407
}
408+
409+
// printf(" => %-15s %-30s %-50s line %d\n", $state, is_int($token) ? token_name($token) : $text, $text, $line);
388410
}
389411

390412
return true;

0 commit comments

Comments
 (0)