public function addAlternate(PhpType $alternate)
 {
     // build() returns the bottom type by default, so we can
     // just bail out early here.
     if ($alternate->isNoType()) {
         return $this;
     }
     $this->isAllType = $this->isAllType || $alternate->isAllType();
     $isAlternateUnknown = $alternate instanceof UnknownType;
     // instanceof is desired here
     $this->isNativeUnknownType = $this->isNativeUnknownType || $isAlternateUnknown;
     if ($isAlternateUnknown) {
         $this->areAllUnknownsChecked = $this->areAllUnknownsChecked && $alternate->isChecked();
     }
     if (!$this->isAllType && !$this->isNativeUnknownType) {
         if ($alternate->isUnionType()) {
             $union = $alternate->toMaybeUnionType();
             foreach ($union->getAlternates() as $unionAlt) {
                 $this->addAlternate($unionAlt);
             }
         } else {
             // Look through the alternates we've got so far,
             // and check if any of them are duplicates of
             // one another.
             foreach ($this->alternates as $index => $current) {
                 // The Unknown type is special in that we cannot use our
                 // subtype based check, but need to check for equality to
                 // avoid duplicates, and not remove all other alternates.
                 if ($alternate->isUnknownType()) {
                     if ($alternate->equals($current)) {
                         return $this;
                     }
                     continue;
                 }
                 // Check if we already have a more general type in the union.
                 // Then, we do not add this alternate.
                 if ($alternate->isSubTypeOf($current)) {
                     return $this;
                 }
                 // Check if we have a subtype of the passed alternate. Then,
                 // we remove that alternate in favor of the newly passed one.
                 if ($current->isSubTypeOf($alternate)) {
                     unset($this->alternates[$index]);
                 }
             }
             $this->alternates[] = $alternate;
         }
     }
     return $this;
 }
 private function restrictByCallable(PhpType $type = null, $outcome)
 {
     if (null === $type) {
         return $outcome ? $this->typeRegistry->getNativeType('callable') : null;
     }
     if ($outcome) {
         if ($type->isUnknownType() || $type->isAllType()) {
             return $this->typeRegistry->getNativeType('callable');
         }
         if ($type->isUnionType()) {
             $types = array();
             foreach ($type->getAlternates() as $altType) {
                 if ($altType->canBeCalled()) {
                     $types[] = $altType;
                 }
             }
             return $this->typeRegistry->createUnionType($types);
         }
         return $type->canBeCalled() ? $type : $this->typeRegistry->getNativeType('none');
     }
     if ($type->isCallableType()) {
         return $this->typeRegistry->getNativeType('none');
     }
     if ($type->isUnionType()) {
         $types = array();
         foreach ($type->getAlternates() as $altType) {
             if ($altType->isCallableType()) {
                 continue;
             }
             $types[] = $altType;
         }
         return $this->typeRegistry->createUnionType($types);
     }
     return $type;
 }
 /**
  * @param string $type
  *
  * @return null|PhpType
  */
 private function tryGettingMoreSpecificType(PhpType $docType = null, PhpType $actualType, AbstractFunction $function, array $importedNamespaces, MethodContainer $container = null, $type)
 {
     if (!$docType) {
         if (!$actualType->isUnknownType() && !$actualType->isAllType()) {
             return null;
         }
         return $this->{'infer' . $type . 'TypeForFunction'}($function, $container);
     }
     // If the type defined by the comment is an object (and not the NoObjectType),
     // and a super type of the actual type, we keep it.
     if (null !== $docType->toMaybeObjectType() && $actualType->isSubtypeOf($docType)) {
         return $docType;
     }
     // If the type defined by the comment is a nullable object type (excluding the
     // NoObject type), we keep the comment that is currently in place.
     if ($this->isNullableObjectType($docType) && $actualType->isSubtypeOf($docType)) {
         return $docType;
     }
     if ($docType->getDocType($importedNamespaces) !== $actualType->getDocType($importedNamespaces)) {
         return $actualType;
     }
     if ($actualType === $this->registry->getNativeType('array')) {
         $inferredType = $this->{'infer' . $type . 'TypeForFunction'}($function, $container);
         if ($inferredType && $inferredType->isArrayType()) {
             return $inferredType;
         }
     }
     return null;
 }
Esempio n. 4
0
 /**
  * Emulates a loose comparison between two types, and returns the result.
  *
  * The result can be true, false, or unknown:
  *
  *     - true: loose comparison of these types is always true
  *     - false: loose comparison of these types is always false
  *     - unknown: outcome depends on the actual values of these types
  *
  * @see http://php.net/manual/en/types.comparisons.php (table with loose comparison ==)
  *
  * @param PhpType $thisType
  * @param PhpType $thatType
  */
 public function testForEquality(PhpType $that)
 {
     if ($that->isAllType() || $that->isUnknownType() || $that->isNoResolvedType() || $this->isAllType() || $this->isUnknownType() || $this->isNoResolvedType()) {
         return TernaryValue::get('unknown');
     }
     if ($this->isNoType() || $that->isNoType()) {
         if ($this->isNoType() && $that->isNoType()) {
             return TernaryValue::get('true');
         }
         return TernaryValue::get('unknown');
     }
     $isThisNumeric = $this->isIntegerType() || $this->isDoubleType();
     $isThatNumeric = $that->isIntegerType() || $that->isDoubleType();
     if (($isThisNumeric || $this->isStringType()) && $that->isArrayType() || ($isThatNumeric || $that->isStringType()) && $this->isArrayType()) {
         return TernaryValue::get('false');
     }
     if ($this->isObjectType() ^ $that->isObjectType()) {
         return TernaryValue::get('false');
     }
     if ($that->isUnionType()) {
         return $that->testForEquality($this);
     }
     if ($this->isArrayType() && $that->isArrayType()) {
         // TODO: Maybe make this more sophisticated by looking at the key,
         //       and element types.
         return TernaryValue::get('unknown');
     }
     // If this is reached, then this base type does not have enough information to
     // make an informed decision, but the method should be overridden by a subtype
     // as this method eventually is never allowed to return null.
     return null;
 }
 private function verifyParamType(PhpType $type, \PHPParser_Node $node, $paramName)
 {
     if (null === ($commentType = $this->parser->getTypeFromParamAnnotation($node, $paramName))) {
         if ($this->getSetting('ask_for_param_type_annotation')) {
             if ($type->isAllType() || $type->isUnknownType()) {
                 $this->phpFile->addComment($node->getLine(), Comment::warning('php_doc.param_type_all_type_non_commented', 'Please add a ``@param`` annotation for parameter ``$%parameter%`` which defines a more specific range of types; something like ``string|array``, or ``null|MyObject``.', array('parameter' => $paramName))->varyIn(array()));
             } else {
                 if ($this->typeRegistry->getNativeType('array')->isSubtypeOf($type)) {
                     $this->phpFile->addComment($node->getLine(), Comment::warning('php_doc.param_type_array_type_not_inferrable', 'Please add a ``@param`` annotation for parameter ``$%parameter%`` which defines the array type; using ``array<SomeType>``, or ``SomeType[]``.', array('parameter' => $paramName))->varyIn(array()));
                 }
             }
         }
         return;
     }
     if (!$this->getSetting('parameters')) {
         return;
     }
     // If the type is not a subtype of the annotated type, then there is an
     // error somewhere (could also be in the type inference engine).
     if (!$type->isSubtypeOf($commentType)) {
         if ($this->getSetting('suggest_more_specific_types') && $this->typeRegistry->getNativeType('array')->isSubtypeOf($type)) {
             $this->phpFile->addComment($this->getLineOfParam($node, $paramName), Comment::warning('php_doc.param_type_mismatch_with_generic_array', 'Should the type for parameter ``$%parameter%`` not be ``%expected_type%``? Also, consider making the array more specific, something like ``array<String>``, or ``String[]``.', array('parameter' => $paramName, 'expected_type' => $type->getDocType($this->importedNamespaces))));
         } else {
             $this->phpFile->addComment($this->getLineOfParam($node, $paramName), Comment::warning('php_doc.param_type_mismatch', 'Should the type for parameter ``$%parameter%`` not be ``%expected_type%``?', array('parameter' => $paramName, 'expected_type' => $type->getDocType($this->importedNamespaces))));
         }
         return;
     }
     if (!$this->getSetting('suggest_more_specific_types')) {
         return;
     }
     if (!$this->isMoreSpecificType($type, $commentType)) {
         if ($type->isAllType()) {
             $this->phpFile->addComment($this->getLineOfParam($node, $paramName), Comment::warning('php_doc.param_type_all_type_more_specific', 'Please define a more specific type for parameter ``$%parameter%``; consider using a union like ``null|Object``, or ``string|array``.', array('parameter' => $paramName))->varyIn(array()));
         } else {
             if ($this->typeRegistry->getNativeType('array')->isSubtypeOf($type)) {
                 $this->phpFile->addComment($this->getLineOfParam($node, $paramName), Comment::warning('php_doc.param_type_array_element_type', 'Please define the element type for the array of parameter ``$%parameter%`` (using ``array<SomeType>``, or ``SomeType[]``).', array('parameter' => $paramName))->varyIn(array()));
             }
         }
         return;
     }
     $this->phpFile->addComment($this->getLineOfParam($node, $paramName), Comment::warning('php_doc.param_type_more_specific', 'Consider making the type for parameter ``$%parameter%`` a bit more specific; maybe use ``%actual_type%``.', array('parameter' => $paramName, $type->getDocType($this->importedNamespaces))));
 }