public function leaveNode(Node $node) { if (!$node instanceof Expr) { return; } $type = null; if ($node instanceof Expr\Assign || $node instanceof Expr\AssignRef) { $type = $node->expr->getAttribute('type'); } elseif ($node instanceof Expr\Ternary) { $exprType1 = $node->if === null ? $node->cond->getAttribute('type') : $node->if->getAttribute('type'); $exprType2 = $node->else->getAttribute('type'); $type = Type::alternatives([$exprType1, $exprType2]); } elseif ($node instanceof Expr\BinaryOp\Coalesce) { $exprType1 = $node->left->getAttribute('type'); $exprType2 = $node->right->getAttribute('type'); $type = Type::alternatives([$exprType1, $exprType2]); } elseif ($node instanceof Expr\ErrorSuppress) { $type = $node->expr->getAttribute('type'); } elseif ($node instanceof Expr\Eval_) { $type = Type::mixed_(); } elseif ($node instanceof Expr\Exit_) { $type = Type::null_(); } if ($type !== null || !$node->hasAttribute('type')) { if ($type === null) { $type = Type::mixed_(); } $node->setAttribute('type', $type); } }
protected function makeMethod($name, $visibility = ClassLike::M_PUBLIC) { return (new Method())->setName($name)->setReturnType(Type::mixed_())->setDocReturnType(Type::mixed_())->setAccessibility($visibility); }
/** * @param ClassLike $class * @param Stmt\ClassLike $node */ protected function processClassLike(ClassLike $class, Stmt\ClassLike $node) { $this->init($class, $node); foreach ($node->stmts as $child) { if ($child instanceof Stmt\ClassConst) { foreach ($child->consts as $constNode) { $const = new ClassConst(); $this->init($const, $constNode); $const->setClass($class); $class->addConst($const); } } elseif ($child instanceof Stmt\Property) { $annotations = []; if ($child->hasAttribute('annotations')) { $annotations = $child->getAttribute('annotations'); } $docTypes = []; if (!empty($annotations['var'])) { foreach ($annotations['var'] as $varTag) { $docTypes[$varTag->getIdentifier()] = $varTag->getType(); } } foreach ($child->props as $propertyNode) { $property = new Property(); $this->init($property, $propertyNode); $property->setName('$' . $property->getName()); $this->processMember($property, $child, $class); $type = Type::mixed_(); if (!empty($docTypes[$property->getName()])) { $type = $docTypes[$property->getName()]; } elseif (count($child->props) === 1 && !empty($docTypes[null])) { $type = $docTypes[null]; } $property->setType($type); $class->addProperty($property); } } elseif ($child instanceof Stmt\ClassMethod) { $method = new Method(); $this->processFunction($method, $child); $this->processMember($method, $child, $class); $method->setAbstract($child->isAbstract()); $method->setFinal($child->isFinal()); $class->addMethod($method); } } }
public function leaveNode(Node $node) { if ($node instanceof Stmt\Function_ || $node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure) { array_pop($this->functionScopeStack); } elseif ($node instanceof Stmt\ClassLike) { array_pop($this->classStack); array_pop($this->functionScopeStack); } if (!$node instanceof Expr) { return; } $type = null; $reflections = null; if ($node instanceof Expr\Variable) { if (is_string($node->name)) { if ($node->name === 'this') { $type = $this->getCurrentClass(); } elseif (array_key_exists('$' . $node->name, $this->getCurrentFunctionScope())) { $type = $this->getCurrentFunctionScope()['$' . $node->name]; } } } elseif ($node instanceof Expr\FuncCall) { $reflections = []; if ($node->name instanceof Name) { $reflections = $this->reflection->findFunction(Type::nameToString($node->name)); } else { $reflections = $this->findMethods($node->name->getAttribute('type'), '__invoke'); } $type = $this->functionsReturnType($reflections); // TODO: ConstFetch } elseif ($node instanceof Expr\MethodCall) { $reflections = []; if (is_string($node->name) || $node->name instanceof Identifier) { $reflections = $this->findMethods($node->var->getAttribute('type'), (string) $node->name); } $type = $this->functionsReturnType($reflections); } elseif ($node instanceof Expr\StaticCall) { $reflections = []; if ($node->class instanceof Name && (is_string($node->name) || $node->name instanceof Identifier)) { $reflections = $this->findMethods(Type::object_(Type::nameToString($node->class)), (string) $node->name, true); } $type = $this->functionsReturnType($reflections); } elseif ($node instanceof Expr\PropertyFetch) { $reflections = []; if (is_string($node->name) || $node->name instanceof Identifier) { $reflections = $this->findProperties($node->var->getAttribute('type'), '$' . (string) $node->name); } $type = $this->variablesType($reflections); } elseif ($node instanceof Expr\StaticPropertyFetch) { $reflections = []; if ($node->class instanceof Name && is_string($node->name)) { $reflections = $this->findProperties(Type::object_(Type::nameToString($node->class)), '$' . $node->name, true); } $type = $this->variablesType($reflections); } elseif ($node instanceof Expr\ClassConstFetch) { // TODO ::class $reflections = []; if ($node->class instanceof Name) { $reflections = $this->findClassConsts(Type::object_(Type::nameToString($node->class)), (string) $node->name); } $type = $this->constsType($reflections); } elseif ($node instanceof Expr\ArrayDimFetch) { $arrayType = $node->var->getAttribute('type'); $altTypes = [$arrayType]; if ($arrayType instanceof AlternativesType) { $altTypes = $arrayType->getAlternatives(); } $types = []; foreach ($altTypes as $altType) { if ($altType instanceof ArrayType) { $types[] = $altType->getValueType(); } elseif ($altType instanceof ObjectType) { $types[] = $this->functionsReturnType($this->findMethods($altType, 'offsetGet')); // TODO: check for ArrayAccess } } $type = $types !== [] ? Type::alternatives($types) : Type::mixed_(); } elseif ($node instanceof Expr\Array_) { $type = Type::array_(); } elseif ($node instanceof Expr\New_) { if ($node->class instanceof Name) { $type = Type::object_(Type::nameToString($node->class)); } else { $type = Type::object_(); } } elseif ($node instanceof Expr\Clone_) { $type = $node->expr->getAttribute('type'); } elseif ($node instanceof Expr\Closure) { $type = Type::object_('\\Closure'); } elseif ($node instanceof Expr\Cast\Array_) { $exprType = $node->expr->getAttribute('type'); $altTypes = [$exprType]; if ($exprType instanceof AlternativesType) { $altTypes = $exprType->getAlternatives(); } $types = []; foreach ($altTypes as $altType) { if ($altType instanceof ArrayType) { $types[] = $altType; } else { $types[] = Type::array_(); // TODO primitives: (array)int --> int[] } } $type = $types !== [] ? Type::alternatives($types) : Type::mixed_(); } elseif ($node instanceof Expr\Cast\Object_) { $exprType = $node->expr->getAttribute('type'); $altTypes = [$exprType]; if ($exprType instanceof AlternativesType) { $altTypes = $exprType->getAlternatives(); } $types = []; foreach ($altTypes as $altType) { if ($altType instanceof ObjectType) { $types[] = $altType; } else { $types[] = Type::object_('\\stdClass'); } } $type = $types !== [] ? Type::alternatives($types) : Type::mixed_(); } elseif ($node instanceof Expr\Include_) { $type = Type::mixed_(); // TODO: Yield_ // TODO: YieldFrom } if ($type !== null) { $node->setAttribute('type', $type); } if ($reflections !== null) { $node->setAttribute('reflections', $reflections); } }
protected function handleFunction(Function_ $function, array $data) { $this->handleElement($function, $data); $function->setReturnType(Type::mixed_()); $function->setDocReturnType($this->getType($data['return_type'])); $function->setReturnByRef($data['return_by_ref']); foreach ($data['params'] as $paramData) { $param = new Param(); $param->setName($paramData['name']); $param->setTypeHint(Type::mixed_()); $param->setDocType($this->getType($paramData['type'])); $param->setOptional($paramData['optional']); $param->setByRef($paramData['by_ref']); $param->setVariadic($paramData['variadic']); $function->addParam($param); } }