Skip to content

Commit

Permalink
Copy doc block types from accessors to properties
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdifabio committed Dec 12, 2018
1 parent 5553715 commit b45b598
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 30 deletions.
4 changes: 3 additions & 1 deletion demo/auto/AutoValue_Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
final class AutoValue_Address extends Address
{
/** @var array */
/** @var string[] */
private $lines;
/** @var ?string */
private $city;
Expand Down Expand Up @@ -64,13 +64,15 @@ 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;
}

public function withCountry(string $country): \AutoValue\Demo\Address
{
$result = clone $this;
unset($result->__memoized);
$result->country = $country;
return $result;
}
Expand Down
3 changes: 3 additions & 0 deletions demo/src/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
36 changes: 26 additions & 10 deletions src/Property.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
<?php
namespace AutoValue;

use phpDocumentor\Reflection\Type;
use Roave\BetterReflection\Reflection\ReflectionMethod;
use Roave\BetterReflection\Reflection\ReflectionProperty;
use Roave\BetterReflection\Reflection\ReflectionType;

/**
* @author Josh Di Fabio <joshdifabio@gmail.com>
*/
class Property
{
public static function named(string $name): self
public static function fromAccessorMethod(string $propertyName, ReflectionMethod $accessorMethod): self
{
$property = new self;
$property->name = $name;
$property->name = $propertyName;
$property->accessorMethod = $accessorMethod;
return $property;
}

Expand All @@ -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()
{
Expand Down
2 changes: 1 addition & 1 deletion src/ValueClass/AccessorMethodProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down
7 changes: 1 addition & 6 deletions src/ValueClass/ValueClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<<THEPHP
/** @var $type */
/** @var {$property->docBlockType()} */
private \${$property->name()};
THEPHP;
}));
Expand Down
20 changes: 10 additions & 10 deletions src/ValueEquals/EqualsMethodProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}),
Expand Down Expand Up @@ -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'));
}
Expand Down
4 changes: 2 additions & 2 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down

0 comments on commit b45b598

Please sign in to comment.