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); }