public function create(Reflector $reflector, PhpDocumentorType $type, array $phpDocTypes, bool $nullable) : Type
 {
     if ($type instanceof String_) {
         return new StringType();
     }
     if ($type instanceof Integer) {
         return new IntegerType();
     }
     if ($type instanceof Float_) {
         return new FloatType();
     }
     if ($type instanceof Boolean) {
         return new BooleanType();
     }
     if ($type instanceof Callable_) {
         return new CallableType();
     }
     if ($type instanceof Void) {
         return new VoidType();
     }
     if ($type instanceof Array_) {
         return $this->decorated->create($reflector, $type, array_unique(array_merge($phpDocTypes, ['array'])), $nullable);
     }
     if (!$type instanceof Object_) {
         return $this->decorated->create($reflector, $type, $phpDocTypes, $nullable);
     }
     if ($type->getFqsen() === null) {
         return new ObjectType();
     }
     return $this->decorated->create($reflector, $type, array_unique(array_merge($phpDocTypes, [$type->getFqsen()->getName()])), $nullable);
 }
 public function create(Reflector $reflector, PhpDocumentorType $type, array $phpDocTypes, bool $nullable) : Type
 {
     if (count($phpDocTypes) > 1 || substr(current($phpDocTypes), -2) === '[]') {
         $nonTraversableDocTypes = array_filter($phpDocTypes, function (string $type) {
             return substr($type, -2) !== '[]';
         });
         $traversableDocTypes = array_diff($phpDocTypes, $nonTraversableDocTypes);
         if (count($traversableDocTypes) > 0 && count($nonTraversableDocTypes) <= 1) {
             $traversableTypes = array_map(function (string $traversablePhpDocType) use($reflector) {
                 return $this->create($reflector, new Mixed(), [substr($traversablePhpDocType, 0, -2)], false);
             }, $traversableDocTypes);
             $decoratedType = count($traversableTypes) > 1 ? new ComposedType(...$traversableTypes) : current($traversableTypes);
             if (count($nonTraversableDocTypes) === 0) {
                 return new IterableType($decoratedType);
             }
             if (current($nonTraversableDocTypes) === 'array') {
                 return new ArrayType($decoratedType);
             }
             $nonTraversableType = $this->create($reflector, new Mixed(), $nonTraversableDocTypes, $nullable);
             if ($nonTraversableType instanceof ClassType && $nonTraversableType->reflection()->implementsInterface(Traversable::class)) {
                 return new CollectionType($nonTraversableType, $decoratedType);
             }
         }
         return new ComposedType(...array_map(function (string $phpDocType) use($type, $nullable, $reflector) {
             return $this->create($reflector, $type, [$phpDocType], $nullable);
         }, $phpDocTypes));
     }
     if (count($phpDocTypes) === 1) {
         switch (current($phpDocTypes)) {
             case 'string':
                 return new StringType();
             case 'integer':
             case 'int':
                 return new IntegerType();
             case 'float':
             case 'double':
                 return new FloatType();
             case 'boolean':
             case 'bool':
             case 'Bool':
                 return new BooleanType();
             case 'callable':
             case 'callback':
                 return new CallableType();
             case 'resource':
                 return new ResourceType();
             case 'object':
                 return new ObjectType();
             case 'void':
                 return new VoidType();
             case 'array':
                 return new ArrayType();
             case 'iterable':
                 return new IterableType();
             default:
                 return new ClassType($reflector->reflectClass(current($phpDocTypes)));
         }
     }
     return $this->decorated->create($reflector, $type, $phpDocTypes, $nullable);
 }
 public function methods() : Methods
 {
     return new Methods($this->name(), array_map(function (ReflectionMethod $method) {
         $returnType = $this->typeFactory->create($this->reflector, $method->getReturnType() ? $method->getReturnType()->getTypeObject() : new Mixed(), $method->getDocBlockReturnTypes(), $method->getReturnType() ? $method->getReturnType()->allowsNull() : false);
         $parameters = array_map(function (ReflectionParameter $parameter) {
             return new Parameter($parameter, $this->typeFactory->create($this->reflector, $parameter->getTypeHint(), $parameter->getDocBlockTypeStrings(), $parameter->allowsNull()), $parameter->getPosition(), $parameter->isOptional());
         }, $method->getParameters());
         return new Method($method, $this->annotations->scanForAnnotations($method->getDocComment(), $this->fileName(), $this->imports), new Parameters($this->name() . '::' . $method->getName(), $parameters), $returnType);
     }, $this->reflectionClass->getMethods()));
 }
 public function create(Reflector $reflector, PhpDocumentorType $type, array $phpDocTypes, bool $nullable) : Type
 {
     if ($type instanceof Mixed && count($phpDocTypes) > 1 && in_array('null', $phpDocTypes)) {
         $nullable = true;
         unset($phpDocTypes[array_search('null', $phpDocTypes)]);
         $phpDocTypes = array_values($phpDocTypes);
     }
     $result = $this->decorated->create($reflector, $type, $phpDocTypes, false);
     if ($nullable && !($result instanceof MixedType || $result instanceof VoidType || $result instanceof NullType)) {
         $result = new NullableType($result);
     }
     return $result;
 }
 function it_does_not_decorate_if_its_not_nullable(Reflector $reflector, TypeFactory $decorated, Type $type)
 {
     $decorated->create($reflector, Argument::any(), Argument::any(), false)->willReturn($type);
     $this->create($reflector, new Mixed(), ['string'], false)->shouldBe($type);
 }
 function it_let_the_decorated_factory_create_the_type_if_no_phpdocs_are_given(Reflector $reflector, TypeFactory $decorated, Type $type)
 {
     $decorated->create($reflector, Argument::any(), Argument::any(), Argument::any())->willReturn($type);
     $this->create($reflector, new Mixed(), [], false)->shouldBe($type);
 }
 function it_might_be_invokable(ReflectionClass $reflectionClass, ReflectionMethod $method, AnnotationScanner $annotations, TypeFactory $typeFactory, Type $type, Annotations $methodAnnotations)
 {
     $reflectionClass->getName()->willReturn('MyClass');
     $method->getName()->willReturn('__invoke');
     $method->getReturnType()->willReturn(null);
     $method->getDocBlockReturnTypes()->willReturn([]);
     $method->getParameters()->willReturn([]);
     $method->getDocComment()->willReturn('');
     $reflectionClass->getMethods()->willReturn([$method]);
     $annotations->scanForAnnotations(Argument::any(), Argument::any(), $this->imports)->willReturn($methodAnnotations);
     $typeFactory->create(Argument::any(), Argument::any(), Argument::any(), Argument::any())->willReturn($type);
     $this->isInvokable()->shouldBe(true);
     $reflectionClass->getMethods()->willReturn([]);
     $this->isInvokable()->shouldBe(false);
 }