Example #1
0
 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);
     }
 }
 public function test_MethodCall_alternatives()
 {
     $method = (new Method())->setDocReturnType(Type::int_())->setStatic(false);
     $refl = $this->getMockBuilder(Reflection::class)->disableOriginalConstructor()->getMock();
     $refl->expects($this->once())->method('findMethod')->with($this->equalTo('\\C'), $this->equalTo('f'))->willReturn($method);
     $var1 = new Expr\Variable('a', ['type' => Type::alternatives([Type::bool_(), Type::object_('\\C')])]);
     $expr = new Expr\MethodCall($var1, 'f');
     $this->infer([$expr], $refl);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::int_()));
 }
Example #3
0
 public function test_Ternary()
 {
     $var1 = new Expr\Variable('a', ['type' => Type::array_()]);
     $var2 = new Expr\Variable('b', ['type' => Type::int_()]);
     $var3 = new Expr\Variable('c', ['type' => Type::float_()]);
     $expr = new Expr\Ternary($var1, $var2, $var3);
     $this->infer([$expr]);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::alternatives([Type::int_(), Type::float_()])));
     $expr = new Expr\Ternary($var1, null, $var3);
     $this->infer([$expr]);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::alternatives([Type::array_(), Type::float_()])));
 }
Example #4
0
 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);
     }
 }
Example #5
0
 /**
  * @param Method[] $methods
  * @param Method   $method
  */
 protected function mergeMethod(array &$methods, Method $method)
 {
     // TODO: Do a better job here?
     $method->setDocReturnType(Type::alternatives([$method->getDocReturnType(), $method->getReturnType()]));
     $params = [];
     foreach ($method->getParams() as $param) {
         $param->setDocType(Type::alternatives([$param->getDocType(), $param->getTypeHint()]));
         $params[$param->getName()] = $param;
     }
     if (isset($methods[strtolower($method->getName())])) {
         $baseMethod = $methods[strtolower($method->getName())];
         $method->setDocReturnType(Type::alternatives([$method->getDocReturnType(), $baseMethod->getDocReturnType()]));
         foreach ($baseMethod->getParams() as $baseParam) {
             if (array_key_exists($baseParam->getName(), $params)) {
                 $param = $params[$baseParam->getName()];
                 $param->setDocType(Type::alternatives([$param->getDocType(), $baseParam->getDocType()]));
             }
         }
     }
     $methods[strtolower($method->getName())] = $method;
 }