*/ protected $class_checkers = []; /** * A lookup table for public namespace constants * * @var array> */ protected static $public_namespace_constants = []; /** * @param Namespace_ $namespace * @param FileChecker $source */ public function __construct(Namespace_ $namespace, FileChecker $source) { $this->source = $source; $this->namespace = $namespace; $this->namespace_name = $this->namespace->name ? implode('\\', $this->namespace->name->parts) : ''; } /** * @return void */ public function visit() { $leftover_stmts = []; self::$public_namespace_constants[$this->namespace_name] = []; $classlike_checkers = []; $namespace_context = new Context($this->source->getFileName()); foreach ($this->namespace->stmts as $stmt) { if ($stmt instanceof PhpParser\Node\Stmt\ClassLike) { $this->visitClassLike($stmt, $classlike_checkers); } elseif ($stmt instanceof PhpParser\Node\Stmt\Use_) { $this->visitUse($stmt); } elseif ($stmt instanceof PhpParser\Node\Stmt\GroupUse) { $this->visitGroupUse($stmt); } elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) { foreach ($stmt->consts as $const) { self::$public_namespace_constants[$this->namespace_name][$const->name] = Type::getMixed(); } $leftover_stmts[] = $stmt; } else { $leftover_stmts[] = $stmt; } } $this->class_checkers = []; foreach ($classlike_checkers as $classlike_checker) { if ($classlike_checker instanceof ClassChecker) { $classlike_checker->visit(null, null); $this->class_checkers[] = $classlike_checker; } elseif ($classlike_checker instanceof InterfaceChecker) { $classlike_checker->visit(); } } if ($leftover_stmts) { $statements_checker = new StatementsChecker($this); $context = new Context($this->source->getFileName()); $statements_checker->analyze($leftover_stmts, $context); } } /** * @param Context $context * @return void */ public function analyze(Context $context) { foreach ($this->class_checkers as $class_checker) { $class_checker->analyze(null, $context); } $this->class_checkers = []; } /** * @param PhpParser\Node\Stmt\ClassLike $stmt * @param array $classlike_checkers * @return void */ public function visitClassLike( PhpParser\Node\Stmt\ClassLike $stmt, array &$classlike_checkers ) { if (!$stmt->name) { throw new \UnexpectedValueException('Did not expect anonymous class here'); } $fq_class_name = ClassLikeChecker::getFQCLNFromString($stmt->name, $this); if ($stmt instanceof PhpParser\Node\Stmt\Class_) { $classlike_checkers[] = new ClassChecker($stmt, $this, $fq_class_name); } elseif ($stmt instanceof PhpParser\Node\Stmt\Interface_) { $classlike_checkers[] = new InterfaceChecker($stmt, $this, $fq_class_name); } elseif ($stmt instanceof PhpParser\Node\Stmt\Trait_) { // register the trait checker new TraitChecker($stmt, $this, $fq_class_name); } } /** * @return string */ public function getNamespace() { return $this->namespace_name; } /** * @param string $const_name * @param Type\Union $const_type * @return void */ public function setConstType($const_name, Type\Union $const_type) { self::$public_namespace_constants[$this->namespace_name][$const_name] = $const_type; } /** * @param string $namespace_name * @param mixed $visibility * @return array */ public static function getConstantsForNamespace($namespace_name, $visibility) { // remove for PHP 7.1 support $visibility = \ReflectionProperty::IS_PUBLIC; // @todo this does not allow for loading in namespace constants not already defined in the current sweep if (!isset(self::$public_namespace_constants[$namespace_name])) { self::$public_namespace_constants[$namespace_name] = []; } if ($visibility === \ReflectionProperty::IS_PUBLIC) { return self::$public_namespace_constants[$namespace_name]; } throw new \InvalidArgumentException('Given $visibility not supported'); } }