function isAllowed($name, ClassLike $inside = null)
 {
     $nameLower = strtolower($name);
     if ($nameLower == "self" && $inside instanceof Class_) {
         return true;
     }
     if ($nameLower != "" && !Util::isLegalNonObject($name)) {
         $class = $this->symbolTable->getAbstractedClass($name);
         if (!$class && !$this->symbolTable->ignoreType($name)) {
             return false;
         }
     }
     return true;
 }
 function index(Config $config, OutputInterface $output, \RecursiveIteratorIterator $it2, $stubs = false)
 {
     $baseDir = $config->getBasePath();
     $symbolTable = $config->getSymbolTable();
     $indexer = new SymbolTableIndexer($symbolTable, $output);
     $traverser1 = new NodeTraverser();
     $traverser1->addVisitor(new NameResolver());
     $traverser2 = new NodeTraverser();
     $traverser2->addVisitor($indexer);
     $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
     $configArr = $config->getConfigArray();
     $count = 0;
     foreach ($it2 as $file) {
         if (($file->getExtension() == "php" || $file->getExtension() == "inc") && $file->isFile()) {
             $name = Util::removeInitialPath($baseDir, $file->getPathname());
             if (strpos($name, "phar://") === 0) {
                 $name = str_replace(\Phar::running(), "", $name);
                 while ($name[0] == '/') {
                     $name = substr($name, 1);
                 }
                 $name = "phar://" . $name;
             }
             try {
                 if (!$stubs && isset($configArr['ignore']) && is_array($configArr['ignore']) && Util::matchesGlobs($baseDir, $file->getPathname(), $configArr['ignore'])) {
                     continue;
                 }
                 ++$count;
                 $output->output(".", " - {$count}:" . $name);
                 // If the $fileName is in our phar then make it a relative path so that files that we index don't
                 // depend on the phar file existing in a particular directory.
                 $fileData = file_get_contents($file->getPathname());
                 if ($config->shouldReindex()) {
                     $symbolTable->removeFileFromIndex($file->getPathname());
                 }
                 $indexer->setFilename($name);
                 $stmts = $parser->parse($fileData);
                 if ($stmts) {
                     $traverser1->traverse($stmts);
                     $traverser2->traverse($stmts);
                 }
             } catch (Error $e) {
                 $output->emitError(__CLASS__, $file, 0, ' Parse Error: ' . $e->getMessage() . "\n");
             }
         }
     }
     return $count;
 }
 /**
  * @param $fileName
  * @param \PhpParser\Node\Expr\New_ $node
  */
 function run($fileName, $node, ClassLike $inside = null, Scope $scope = null)
 {
     if ($node->class instanceof Name) {
         $name = $node->class->toString();
         if (strcasecmp($name, "self") != 0 && strcasecmp($name, "static") != 0 && !$this->symbolTable->ignoreType($name)) {
             $this->incTests();
             $class = $this->symbolTable->getAbstractedClass($name);
             if (!$class) {
                 $this->emitError($fileName, $node, self::TYPE_UNKNOWN_CLASS, "Attempt to instantiate unknown class {$name}");
                 return;
             }
             if ($class->isDeclaredAbstract()) {
                 $this->emitError($fileName, $node, self::TYPE_SIGNATURE_TYPE, "Attempt to instantiate abstract class {$name}");
                 return;
             }
             $method = Util::findAbstractedMethod($name, "__construct", $this->symbolTable);
             if (!$method) {
                 $minParams = $maxParams = 0;
             } else {
                 if ($method->getAccessLevel() == "private" && (!$inside || strcasecmp($inside->namespacedName, $name) != 0)) {
                     $this->emitError($fileName, $node, self::TYPE_SCOPE_ERROR, "Attempt to call private constructor outside of class {$name}");
                     return;
                 }
                 $maxParams = count($method->getParameters());
                 $minParams = $method->getMinimumRequiredParameters();
                 if (strcasecmp("imagick", $name) == 0) {
                     $minParams = 0;
                     $maxParams = 1;
                 }
             }
             $passedArgCount = count($node->args);
             if ($passedArgCount < $minParams) {
                 $this->emitError($fileName, $node, self::TYPE_SIGNATURE_COUNT, "Call to {$name}::__construct passing {$passedArgCount} count, required count={$minParams}");
             }
             if ($passedArgCount > $maxParams) {
                 //$this->emitError($fileName, $node, "Parameter mismatch","Call to $name::__construct passing too many parameters ($passedArgCount instead of $maxParams)");
             }
         }
     }
 }
 /**
  * @param                                    $fileName
  * @param \PhpParser\Node\Expr\MethodCall $node
  */
 function run($fileName, $node, ClassLike $inside = null, Scope $scope = null)
 {
     if ($inside instanceof Trait_) {
         // Traits should be converted into methods in the class, so that we can check them in context.
         return;
     }
     if ($node->name instanceof Expr) {
         // Variable method name.  Yuck!
         return;
     }
     $methodName = strval($node->name);
     $varName = "{expr}";
     $className = "";
     if ($node->var instanceof Variable && $node->var->name == "this" && !$inside) {
         $this->emitError($fileName, $node, self::TYPE_SCOPE_ERROR, "Can't use \$this outside of a class");
         return;
     }
     if ($scope) {
         $className = $this->inferenceEngine->inferType($inside, $node->var, $scope);
     }
     if ($className != "" && $className[0] != "!") {
         if (!$this->symbolTable->getAbstractedClass($className)) {
             $this->emitError($fileName, $node, self::TYPE_UNKNOWN_CLASS, "Unknown class {$className} in method call to {$methodName}()");
             return;
         }
         //echo $fileName." ".$node->getLine(). " : Looking up $className->$methodName\n";
         $method = Util::findAbstractedSignature($className, $methodName, $this->symbolTable);
         if ($method) {
             $this->checkMethod($fileName, $node, $className, $scope, $method);
         } else {
             // If there is a magic __call method, then we can't know if it will handle these calls.
             if (!Util::findAbstractedMethod($className, "__call", $this->symbolTable) && !$this->symbolTable->isParentClassOrInterface("iteratoriterator", $className)) {
                 $this->emitError($fileName, $node, self::TYPE_UNKNOWN_METHOD, "Call to unknown method of {$className}: \$" . $varName . "->" . $methodName);
             }
         }
     }
 }
 function phase2(Config $config, OutputInterface $output, $toProcess)
 {
     $traverser1 = new NodeTraverser();
     $traverser1->addVisitor(new DocBlockNameResolver());
     $analyzer = new StaticAnalyzer($config->getBasePath(), $config->getSymbolTable(), $output, $config);
     $traverser2 = new NodeTraverser();
     $traverser2->addVisitor(new TraitImportingVisitor($config->getSymbolTable()));
     $traverser3 = new NodeTraverser();
     $traverser3->addVisitor($analyzer);
     $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
     $processingCount = 0;
     foreach ($toProcess as $file) {
         try {
             $name = Util::removeInitialPath($config->getBasePath(), $file);
             $output->output(".", $name);
             $processingCount++;
             //echo " - $processingCount:" . $file . "\n";
             $fileData = file_get_contents($file);
             $stmts = $parser->parse($fileData);
             if ($stmts) {
                 $analyzer->setFile($name);
                 $stmts = $traverser1->traverse($stmts);
                 $stmts = $traverser2->traverse($stmts);
                 $traverser3->traverse($stmts);
             }
         } catch (Error $e) {
             $output->emitError(__CLASS__, $file, 0, "Parse error", $e->getMessage());
         } catch (\Guardrail\Exceptions\UnknownTraitException $e) {
             $output->emitError(__CLASS__, $file, 0, "Unknown trait error", $e->getMessage());
         }
     }
     if ($output instanceof XUnitOutput) {
         //	print_r($output->getCounts());
     }
     return $output->getErrorCount() > 0 ? 1 : 0;
 }
 /**
  * @param                                    $fileName
  * @param \PhpParser\Node\Expr\PropertyFetch $node
  */
 function run($fileName, $node, ClassLike $inside = null, Scope $scope = null)
 {
     if ($node->var instanceof Variable) {
         if (is_string($node->var->name) && $node->var->name == 'this') {
             if ($inside instanceof Trait_) {
                 return;
             }
             if (!$inside) {
                 $this->emitError($fileName, $node, self::TYPE_SCOPE_ERROR, "Can't use \$this outside of a class");
                 return;
             }
             if (!is_string($node->name)) {
                 // Variable method name.  Yuck!
                 return;
             }
             //echo "Access ".$node->var->name."->".$node->name."\n";
             $property = Util::findProperty($inside, $node->name, $this->symbolTable);
             if (!$property) {
                 //$this->emitError($fileName, $node, "Unknown property", "Accessing unknown property of $inside->namespacedName: \$this->" . $node->name);
                 return;
             }
         }
     }
 }
 /**
  * @param $fileName
  * @param \PhpParser\Node\Stmt\Class_ $node
  */
 function run($fileName, $node, ClassLike $inside = null, Scope $scope = null)
 {
     if ($node->implements) {
         $arr = is_array($node->implements) ? $node->implements : [$node->implements];
         foreach ($arr as $interface) {
             $name = $interface->toString();
             $this->incTests();
             if ($name) {
                 $interface = $this->symbolTable->getAbstractedClass($name);
                 if (!$interface) {
                     $this->emitError($fileName, $node, self::TYPE_UNKNOWN_CLASS, $node->name . " implements unknown interface " . $name);
                 } else {
                     // Don't force abstract classes to implement all methods.
                     if (!$node->isAbstract()) {
                         foreach ($interface->getMethodNames() as $interfaceMethod) {
                             $classMethod = $this->implementsMethod($fileName, $node, $interfaceMethod);
                             if (!$classMethod) {
                                 if (!$node->isAbstract()) {
                                     $this->emitError($fileName, $node, self::TYPE_UNIMPLEMENTED_METHOD, $node->name . " does not implement method " . $interfaceMethod);
                                 }
                             } else {
                                 $this->checkMethod($node, $classMethod, $interface, $interface->getMethod($interfaceMethod));
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($node->extends) {
         $class = new \Guardrail\Abstractions\Class_($node);
         $parentClass = $this->symbolTable->getAbstractedClass($node->extends);
         if (!$parentClass) {
             $this->emitError($fileName, $node->extends, self::TYPE_UNKNOWN_CLASS, "Unable to find parent " . $node->extends);
         }
         foreach ($class->getMethodNames() as $methodName) {
             if ($methodName != "__construct") {
                 $method = Util::findAbstractedMethod($node->extends, $methodName, $this->symbolTable);
                 if ($method) {
                     $this->checkMethod($node, $class->getMethod($methodName), $parentClass, $method);
                 }
             }
         }
     }
 }
 function getAbstractedMethod($className, $methodName)
 {
     $cacheName = strtolower($className . "::" . $methodName);
     $ob = $this->cache->get("AClassMethod:" . $cacheName);
     if (!$ob) {
         $ob = \Guardrail\Util::findAbstractedMethod($className, $methodName, $this);
         if (!$ob && strpos($className, "\\") === false) {
             try {
                 $refl = new \ReflectionMethod($className, $methodName);
                 $ob = new \Guardrail\Abstractions\ReflectedClassMethod($refl);
             } catch (\ReflectionException $e) {
                 $ob = null;
             }
         }
         if ($ob) {
             $this->cache->add("AClassMethod:" . $cacheName, $ob);
         }
     }
     return $ob;
 }
 function getAccessLevel()
 {
     return Util::getMethodAccessLevel($this->method);
 }
 function getConstructorDependencies($className)
 {
     $method = Util::findAbstractedMethod($className, "__construct", $this->symbolTable);
     $dependencies = [];
     if ($method) {
         foreach ($method->getParameters() as $param) {
             $dependencies[] = strval($param->getType());
         }
     }
     return $dependencies;
 }
 /**
  * @param $fileName
  * @param \PhpParser\Node\Expr\StaticCall $call
  */
 function run($fileName, $call, ClassLike $inside = null, Scope $scope = null)
 {
     if ($call->class instanceof Name && gettype($call->name) == "string") {
         $name = $call->class->toString();
         if ($this->symbolTable->ignoreType($name)) {
             return;
         }
         $originalName = $name;
         $possibleDynamic = false;
         switch (strtolower($name)) {
             case 'self':
                 $possibleDynamic = true;
                 // Fall through
             // Fall through
             case 'static':
                 if (!$inside) {
                     $this->emitError($fileName, $call, self::TYPE_SCOPE_ERROR, "Can't access using self:: outside of a class");
                     return;
                 }
                 $name = $inside->namespacedName;
                 break;
             case 'parent':
                 if (!$inside) {
                     $this->emitError($fileName, $call, self::TYPE_SCOPE_ERROR, "Can't access using parent:: outside of a class");
                     return;
                 }
                 $possibleDynamic = true;
                 if ($inside->extends) {
                     $name = strval($inside->extends);
                 } else {
                     $this->emitError($fileName, $call, self::TYPE_SCOPE_ERROR, "Can't access using parent:: in a class with no parent");
                     return;
                 }
                 break;
             default:
                 if ($inside) {
                     $currentClass = strval($inside->namespacedName);
                     if ($this->symbolTable->isParentClassOrInterface($name, $currentClass)) {
                         $possibleDynamic = true;
                     }
                 }
                 break;
         }
         $this->incTests();
         $class = $this->symbolTable->getAbstractedClass($name);
         if (!$class) {
             if (!$this->symbolTable->ignoreType($name)) {
                 $this->emitError($fileName, $call, self::TYPE_UNKNOWN_CLASS, "Static call to unknown class {$name}::" . $call->name);
             }
         } else {
             $method = Util::findAbstractedMethod($name, $call->name, $this->symbolTable);
             if ($call->name == "__construct" && !$method) {
                 // Find a PHP 4 style constructor (function name == class name)
                 $method = Util::findAbstractedMethod($name, $name, $this->symbolTable);
             }
             if (!$method) {
                 if (!Util::findAbstractedMethod($name, "__callStatic", $this->symbolTable) && (!$possibleDynamic || !Util::findAbstractedMethod($name, "__call", $this->symbolTable))) {
                     $this->emitError($fileName, $call, self::TYPE_UNKNOWN_METHOD, "Unable to find method.  {$name}::" . $call->name);
                 }
             } else {
                 if (!$method->isStatic()) {
                     if (!$scope->isStatic() && $possibleDynamic) {
                         if ($call->name != "__construct" && $call->class != "parent") {
                             // echo "Static call in $fileName " . $call->getLine() . "\n";
                         }
                     } else {
                         $this->emitError($fileName, $call, self::TYPE_INCORRECT_DYNAMIC_CALL, "Attempt to call non-static method: {$name}::" . $call->name . " statically");
                     }
                 }
                 $minimumParams = $method->getMinimumRequiredParameters();
                 if (count($call->args) < $minimumParams) {
                     $this->emitError($fileName, $call, self::TYPE_SIGNATURE_COUNT, "Static call to method {$name}::" . $call->name . " does not pass enough parameters (" . count($call->args) . " passed {$minimumParams} required)");
                 }
             }
         }
     }
 }
Beispiel #12
0
 static function isLegalNonObject($name)
 {
     return Util::isScalarType($name) || strcasecmp($name, "callable") == 0 || strcasecmp($name, "array") == 0;
 }
 function inferPropertyFetch(Node\Expr\PropertyFetch $expr, $inside, $scope)
 {
     $class = $this->inferType($inside, $expr->var, $scope);
     if (!empty($class) && $class[0] != "!") {
         if (gettype($expr->name) == 'string') {
             $classDef = $this->index->getClass($class);
             if ($classDef) {
                 $prop = Util::findProperty($classDef, strval($expr->name), $this->index);
                 if ($prop) {
                     /*
                     						$type = $prop->getAttribute("namespacedType") ?: "";
                     						if(!empty($type)) {
                     							if($type[0]=='\\') {
                     								$type=substr($type,1);
                     							}
                     							return $type;
                     						}*/
                 }
             }
         }
     }
     return Scope::MIXED_TYPE;
 }