|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace frdl\Lint; |
| 4 | + |
| 5 | +class Php |
| 6 | +{ |
| 7 | + protected $cacheDir = null; |
| 8 | + |
| 9 | + public function __construct($cacheDir = null) |
| 10 | + { |
| 11 | + $this->cacheDir = $cacheDir; |
| 12 | + } |
| 13 | + |
| 14 | + public function setCacheDir($cacheDir = null) |
| 15 | + { |
| 16 | + $this->cacheDir = $cacheDir; |
| 17 | + return $this; |
| 18 | + } |
| 19 | + |
| 20 | + public function getCacheDir() |
| 21 | + { |
| 22 | + if((null!==$this->cacheDir && !empty($this->cacheDir)) && is_dir($this->cacheDir)){ |
| 23 | + return $this->cacheDir; |
| 24 | + } |
| 25 | + |
| 26 | + if(!isset($_ENV['FRDL_HPS_CACHE_DIR']))$_ENV['FRDL_HPS_CACHE_DIR']=getenv('FRDL_HPS_CACHE_DIR'); |
| 27 | + |
| 28 | + $this->cacheDir = |
| 29 | + ( isset($_ENV['FRDL_HPS_CACHE_DIR']) && !empty($_ENV['FRDL_HPS_CACHE_DIR'])) |
| 30 | + ? $_ENV['FRDL_HPS_CACHE_DIR'] |
| 31 | + : \sys_get_temp_dir() . \DIRECTORY_SEPARATOR . \get_current_user(). \DIRECTORY_SEPARATOR . 'cache' . \DIRECTORY_SEPARATOR ; |
| 32 | + |
| 33 | + $this->cacheDir = rtrim($this->cacheDir, '\\/'). \DIRECTORY_SEPARATOR.'lint'; |
| 34 | + |
| 35 | + if(!is_dir($this->cacheDir)){ |
| 36 | + mkdir($this->cacheDir, 0755, true); |
| 37 | + } |
| 38 | + |
| 39 | + |
| 40 | + return $this->cacheDir; |
| 41 | + } |
| 42 | + |
| 43 | + public function lintString($source) |
| 44 | + { |
| 45 | + $cachedir = $this->getCacheDir(); |
| 46 | + if(!is_writable($cachedir)){ |
| 47 | + mkdir($cachedir, 0755, true); |
| 48 | + } |
| 49 | + $tmpfname = tempnam($cachedir, 'frdl_lint_php'); |
| 50 | + if(empty($tmpfname))return false; |
| 51 | + file_put_contents($tmpfname, $source); |
| 52 | + $valid = $this->checkSyntax($tmpfname, false); |
| 53 | + unlink($tmpfname); |
| 54 | + return $valid; |
| 55 | + } |
| 56 | + |
| 57 | + public function lintFile($fileName, $checkIncludes = true) |
| 58 | + { |
| 59 | + return call_user_func_array([$this, 'checkSyntax'], [$fileName, $checkIncludes]); |
| 60 | + } |
| 61 | + |
| 62 | + public static function lintStringStatic($source) |
| 63 | + { |
| 64 | + $o = new self; |
| 65 | + $tmpfname = tempnam($o->getCacheDir(), 'frdl_lint_php'); |
| 66 | + file_put_contents($tmpfname, $source); |
| 67 | + $valid = $o->checkSyntax($tmpfname, false); |
| 68 | + unlink($tmpfname); |
| 69 | + return $valid; |
| 70 | + } |
| 71 | + |
| 72 | + public static function lintFileStatic($fileName, $checkIncludes = true) |
| 73 | + { |
| 74 | + $o = new self; |
| 75 | + $o->setCacheDir($o->getCacheDir()); |
| 76 | + return call_user_func_array([$o, 'checkSyntax'], [$fileName, $checkIncludes]); |
| 77 | + } |
| 78 | + |
| 79 | + public static function __callStatic($name, $arguments) |
| 80 | + { |
| 81 | + $o = new self; |
| 82 | + return call_user_func_array([$o, $name], $arguments); |
| 83 | + } |
| 84 | + |
| 85 | + public function checkSyntax($fileName, $checkIncludes = false) |
| 86 | + { |
| 87 | + if(!file_exists($fileName)) |
| 88 | + throw new \Exception("Cannot read file ".$fileName); |
| 89 | + |
| 90 | + // Sort out the formatting of the filename |
| 91 | + $fileName = realpath($fileName); |
| 92 | + |
| 93 | + // Get the shell output from the syntax check command |
| 94 | + $output = shell_exec(sprintf('%s -l "%s"', (new \webfan\hps\patch\PhpBinFinder())->find(), $fileName)); |
| 95 | + |
| 96 | + // Try to find the parse error text and chop it off |
| 97 | + $syntaxError = preg_replace("/Errors parsing.*$/", "", $output, -1, $count); |
| 98 | + |
| 99 | + // If the error text above was matched, throw an exception containing the syntax error |
| 100 | + if($count > 0) |
| 101 | + //throw new \Exception(trim($syntaxError)); |
| 102 | + return 'Errors parsing '.print_r([$output, $count],true); |
| 103 | + |
| 104 | + // If we are going to check the files includes |
| 105 | + if($checkIncludes) |
| 106 | + { |
| 107 | + foreach($this->getIncludes($fileName) as $include) |
| 108 | + { |
| 109 | + // Check the syntax for each include |
| 110 | + $tCheck = $this->checkSyntax($include, $checkIncludes); |
| 111 | + if(true!==$tCheck){ |
| 112 | + return $tCheck; |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + return true; |
| 118 | + } |
| 119 | + |
| 120 | + public function getIncludes($fileName) |
| 121 | + { |
| 122 | + $includes = array(); |
| 123 | + // Get the directory name of the file so we can prepend it to relative paths |
| 124 | + $dir = dirname($fileName); |
| 125 | + |
| 126 | + // Split the contents of $fileName about requires and includes |
| 127 | + // We need to slice off the first element since that is the text up to the first include/require |
| 128 | + $requireSplit = array_slice(preg_split('/require|include/i', file_get_contents($fileName)), 1); |
| 129 | + |
| 130 | + // For each match |
| 131 | + foreach($requireSplit as $string) |
| 132 | + { |
| 133 | + // Substring up to the end of the first line, i.e. the line that the require is on |
| 134 | + $string = substr($string, 0, strpos($string, ";")); |
| 135 | + |
| 136 | + // If the line contains a reference to a variable, then we cannot analyse it |
| 137 | + // so skip this iteration |
| 138 | + if(strpos($string, "$") !== false) |
| 139 | + continue; |
| 140 | + |
| 141 | + // Split the string about single and double quotes |
| 142 | + $quoteSplit = preg_split('/[\'"]/', $string); |
| 143 | + |
| 144 | + // The value of the include is the second element of the array |
| 145 | + // Putting this in an if statement enforces the presence of '' or "" somewhere in the include |
| 146 | + // includes with any kind of run-time variable in have been excluded earlier |
| 147 | + // this just leaves includes with constants in, which we can't do much about |
| 148 | + if($include = $quoteSplit[1]) |
| 149 | + { |
| 150 | + // If the path is not absolute, add the dir and separator |
| 151 | + // Then call realpath to chop out extra separators |
| 152 | + if(strpos($include, ':') === FALSE) |
| 153 | + $include = realpath($dir.\DIRECTORY_SEPARATOR.$include); |
| 154 | + |
| 155 | + array_push($includes, $include); |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + return $includes; |
| 160 | + } |
| 161 | +} |
0 commit comments