diff --git a/demo/auto/AutoValue_Address.php b/demo/auto/AutoValue_Address.php index d5a16ca..6e7e144 100644 --- a/demo/auto/AutoValue_Address.php +++ b/demo/auto/AutoValue_Address.php @@ -6,7 +6,7 @@ */ final class AutoValue_Address extends Address { - /** @var array */ + /** @var string[] */ private $lines; /** @var ?string */ private $city; @@ -64,6 +64,7 @@ public function toBuilder(): \AutoValue\Demo\AddressBuilder public function withLines(string ...$lines): \AutoValue\Demo\Address { $result = clone $this; + unset($result->__memoized); $result->lines = $lines; return $result; } @@ -71,6 +72,7 @@ public function withLines(string ...$lines): \AutoValue\Demo\Address public function withCountry(string $country): \AutoValue\Demo\Address { $result = clone $this; + unset($result->__memoized); $result->country = $country; return $result; } diff --git a/demo/src/Address.php b/demo/src/Address.php index a050c43..5d1472d 100644 --- a/demo/src/Address.php +++ b/demo/src/Address.php @@ -13,6 +13,9 @@ public static function builder(): AddressBuilder public abstract function toBuilder(): AddressBuilder; + /** + * @return string[] + */ public abstract function lines(): array; public abstract function withLines(string ...$lines): self; diff --git a/src/Property.php b/src/Property.php index 0b21c70..5032795 100644 --- a/src/Property.php +++ b/src/Property.php @@ -1,6 +1,9 @@ name = $name; + $property->name = $propertyName; + $property->accessorMethod = $accessorMethod; return $property; } @@ -20,26 +24,38 @@ public function name(): string return $this->name; } - public function type(): ?ReflectionType + public function phpType(): ?ReflectionType { - return $this->type; + if (!$this->phpType) { + $this->phpType = $this->accessorMethod->getReturnType(); + } + return $this->phpType; } - public function withType(?ReflectionType $type): self + public function docBlockType(): string { - $result = clone $this; - $result->type = $type; - return $result; + if (!isset($this->docBlockType)) { + $docBlockTypes = $this->accessorMethod->getDocBlockReturnTypes(); + $this->docBlockType = $docBlockTypes + ? \implode('|', $docBlockTypes) + : ($this->phpType() + ? generateTypeHint($this->phpType(), $this->accessorMethod->getDeclaringClass()) + : 'mixed' + ); + } + return $this->docBlockType; } public function isRequired(): bool { - return $this->type && !$this->type->allowsNull(); + return $this->phpType() && !$this->phpType()->allowsNull(); } private $name; + /** @var ReflectionMethod */ + private $accessorMethod; /** @var ReflectionType|null */ - private $type; + private $phpType; private function __construct() { diff --git a/src/ValueClass/AccessorMethodProcessor.php b/src/ValueClass/AccessorMethodProcessor.php index d287396..50ef081 100644 --- a/src/ValueClass/AccessorMethodProcessor.php +++ b/src/ValueClass/AccessorMethodProcessor.php @@ -29,7 +29,7 @@ public function inferProperties(ReflectionMethodCollection $matchedMethods): Pro $templateUsesPrefixes = self::templateUsesPrefixes($matchedMethods); return $matchedMethods->reduce(PropertyCollection::create(), function (PropertyCollection $properties, ReflectionMethod $method) use ($templateUsesPrefixes) { $propertyName = self::getPropertyName($templateUsesPrefixes, $method); - $property = Property::named($propertyName)->withType($method->getReturnType()); + $property = Property::fromAccessorMethod($propertyName, $method); return $properties->withProperty($property); }); } diff --git a/src/ValueClass/ValueClassGenerator.php b/src/ValueClass/ValueClassGenerator.php index 0cd2815..3cda897 100644 --- a/src/ValueClass/ValueClassGenerator.php +++ b/src/ValueClass/ValueClassGenerator.php @@ -18,13 +18,8 @@ public function generateClass(ReflectionClass $baseClass, PropertyCollection $pr { // todo include type in properties constant $propertyDeclarations = \implode("\n", $properties->map(function (Property $property) use ($baseClass) { - if ($property->type()) { - $type = generateTypeHint($property->type(), $baseClass); - } else { - $type = 'mixed'; - } return <<docBlockType()} */ private \${$property->name()}; THEPHP; })); diff --git a/src/ValueEquals/EqualsMethodProcessor.php b/src/ValueEquals/EqualsMethodProcessor.php index 55cee84..3434e26 100644 --- a/src/ValueEquals/EqualsMethodProcessor.php +++ b/src/ValueEquals/EqualsMethodProcessor.php @@ -50,25 +50,25 @@ private function generateMethodBody( string $valueParam, PropertyCollection $properties ): string { - $typedProperties = $properties->filter(function (Property $property) { return $property->type() !== null; }); - $arrayProperties = $typedProperties->filter(function (Property $property) { return (string)$property->type() === 'array'; }); - $classProperties = $typedProperties->filter(function (Property $property) { return isClass($property->type()); }); + $typedProperties = $properties->filter(function (Property $property) { return $property->phpType() !== null; }); + $arrayProperties = $typedProperties->filter(function (Property $property) { return (string)$property->phpType() === 'array'; }); + $classProperties = $typedProperties->filter(function (Property $property) { return isClass($property->phpType()); }); $valueObjectProperties = $classProperties->filter(function (Property $property) use ($templateClass) { return $this->isValueObject($templateClass, $property); }); $mixedProperties = $properties->filter(function (Property $property) { - return $property->type() === null - || (string)$property->type() === 'object' - || (string)$property->type() === 'iterable' - || (string)$property->type() === 'callable'; + return $property->phpType() === null + || (string)$property->phpType() === 'object' + || (string)$property->phpType() === 'iterable' + || (string)$property->phpType() === 'callable'; }); $testsForTypedProperties = \array_merge( ["\${$valueParam} instanceof self"], $typedProperties - ->filter(function (Property $property) { return !isClass($property->type()); }) - ->filter(function (Property $property) { return (string)$property->type() !== 'array'; }) + ->filter(function (Property $property) { return !isClass($property->phpType()); }) + ->filter(function (Property $property) { return (string)$property->phpType() !== 'array'; }) ->mapPropertyNames(function (string $propertyName) use ($valueParam) { return "\$this->$propertyName === \${$valueParam}->$propertyName"; }), @@ -137,7 +137,7 @@ private function generateMethodBody( private function isValueObject(ReflectionClass $templateClass, Property $property): bool { - $reflectionClass = getClass($templateClass, $property->type()); + $reflectionClass = getClass($templateClass, $property->phpType()); return $reflectionClass->hasMethod('equals') && $this->matchMethod($reflectionClass->getMethod('equals')); } diff --git a/src/functions.php b/src/functions.php index c62f8bb..607a3b3 100644 --- a/src/functions.php +++ b/src/functions.php @@ -35,12 +35,12 @@ function generateParameters(array $reflectionParameters): string }, $reflectionParameters)); } -function generateReturnTypeHint(?ReflectionType $returnType, ?ReflectionClass $declaringClass): string +function generateReturnTypeHint(?ReflectionType $returnType, ReflectionClass $declaringClass): string { return $returnType === null ? '' : ': ' . generateTypeHint($returnType, $declaringClass); } -function generateTypeHint(ReflectionType $type, ?ReflectionClass $declaringClass): string +function generateTypeHint(ReflectionType $type, ReflectionClass $declaringClass): string { $prefix = $type->allowsNull() ? '?' : ''; $normalizedType = (string)$type === 'self'