While developing an app where I have to include PHP files written by a user, I came across the following problem:
I used "php -l somefile.php" to check the syntax of the file I was about to include and if it passed, I would include it - so far so good. But in some test cases, the file I was including would have other includes/requires inside it. If one of these was invalid, then I would still get the parse error that I was trying to avoid.
I got round it using this:
<?php
function CheckSyntax($fileName, $checkIncludes = true)
{
if(!is_file($fileName) || !is_readable($fileName))
throw new Exception("Cannot read file ".$fileName);
$fileName = realpath($fileName);
$output = shell_exec('php -l "'.$fileName.'"');
$syntaxError = preg_replace("/Errors parsing.*$/", "", $output, -1, $count);
if($count > 0)
throw new Exception(trim($syntaxError));
if($checkIncludes)
{
foreach(GetIncludes($fileName) as $include)
{
CheckSyntax($include);
}
}
}
function GetIncludes($fileName)
{
$includes = array();
$dir = dirname($fileName);
$requireSplit = array_slice(preg_split('/require|include/i', file_get_contents($fileName)), 1);
foreach($requireSplit as $string)
{
$string = substr($string, 0, strpos($string, ";"));
if(strpos($string, "$") !== false)
continue;
$quoteSplit = preg_split('/[\'"]/', $string);
if($include = $quoteSplit[1])
{
if(strpos($include, ':') === FALSE)
$include = realpath($dir.DIRECTORY_SEPARATOR.$include);
array_push($includes, $include);
}
}
return $includes;
}
?>
This checks as many of the includes inside the file as it possibly can without executing anything.