Skip to content

Commit

Permalink
Replace type system
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Jan 19, 2024
1 parent a32f364 commit 05a1098
Show file tree
Hide file tree
Showing 53 changed files with 1,728 additions and 1,112 deletions.
60 changes: 60 additions & 0 deletions benchmarks/TestBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

use Carbon\CarbonImmutable;
use Illuminate\Support\Collection;
use Orchestra\Testbench\Concerns\CreatesApplication;
use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use Spatie\LaravelData\LaravelDataServiceProvider;
use Spatie\LaravelData\Optional;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\Types\Storage\AcceptedTypesStorage;
use Spatie\LaravelData\Tests\Fakes\ComplicatedData;
use Spatie\LaravelData\Tests\Fakes\SimpleData;

class TestBench
{
use CreatesApplication;

public function __construct()
{
$this->createApplication();
}

protected function getPackageProviders($app)
{
return [
LaravelDataServiceProvider::class,
];
}

#[Revs(5000), Iterations(5)]
public function benchUseStored()
{
for ($i = 0; $i < 100; $i++) {
$this->runStored();
}
}

protected function runStored(): array
{
return AcceptedTypesStorage::getAcceptedTypes(Collection::class);
}

#[Revs(5000), Iterations(5)]
public function benchUseNative()
{
for ($i = 0; $i < 100; $i++) {
$this->runNative();
}
}

protected function runNative(): array
{
return ! class_exists(Collection::class) ? [] : array_unique([
...array_values(class_parents(Collection::class)),
...array_values(class_implements(Collection::class)),
]);
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
}
],
"require" : {
"php": "^8.1",
"php": "^8.2",
"illuminate/contracts": "^10.0",
"phpdocumentor/type-resolver": "^1.5",
"spatie/laravel-package-tools": "^1.9.0",
Expand Down
25 changes: 0 additions & 25 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -125,36 +125,11 @@ parameters:
count: 1
path: src/Support/DataConfig.php

-
message: "#^Match expression does not handle remaining values\\: \\(class\\-string\\<ReflectionIntersectionType\\>&literal\\-string\\)\\|\\(class\\-string\\<ReflectionUnionType\\>&literal\\-string\\)$#"
count: 1
path: src/Support/Factories/DataTypeFactory.php

-
message: "#^Match expression does not handle remaining values\\: \\(class\\-string\\<ReflectionParameter\\>&literal\\-string\\)\\|\\(class\\-string\\<ReflectionProperty\\>&literal\\-string\\)$#"
count: 1
path: src/Support/Factories/DataTypeFactory.php

-
message: "#^Parameter \\#1 \\$storage of method SplObjectStorage\\<Spatie\\\\LaravelData\\\\Support\\\\Partials\\\\Partial,null\\>\\:\\:removeAll\\(\\) expects SplObjectStorage\\<object, mixed\\>, Spatie\\\\LaravelData\\\\Support\\\\Partials\\\\PartialsCollection given\\.$#"
count: 1
path: src/Support/Transformation/DataContext.php

-
message: "#^Call to an undefined method ReflectionType\\:\\:getName\\(\\)\\.$#"
count: 2
path: src/Support/Types/MultiType.php

-
message: "#^Parameter \\#1 \\$type of static method Spatie\\\\LaravelData\\\\Support\\\\Types\\\\PartialType\\:\\:create\\(\\) expects ReflectionNamedType, ReflectionType given\\.$#"
count: 1
path: src/Support/Types/MultiType.php

-
message: "#^Unsafe usage of new static\\(\\)\\.$#"
count: 1
path: src/Support/Types/MultiType.php

-
message: "#^Call to an undefined method DateTimeInterface\\:\\:setTimezone\\(\\)\\.$#"
count: 1
Expand Down
5 changes: 3 additions & 2 deletions src/Commands/DataStructuresCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Spatie\LaravelData\Support\Caching\DataStructureCache;
use Spatie\LaravelData\Support\DataClass;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\Factories\DataClassFactory;

class DataStructuresCacheCommand extends Command
{
Expand All @@ -18,7 +19,7 @@ class DataStructuresCacheCommand extends Command

public function handle(
DataStructureCache $dataStructureCache,
DataConfig $dataConfig
DataClassFactory $dataClassFactory,
): void {
$this->components->info('Caching data structures...');

Expand All @@ -31,7 +32,7 @@ public function handle(
$progressBar = $this->output->createProgressBar(count($dataClasses));

foreach ($dataClasses as $dataClassString) {
$dataClass = DataClass::create(new ReflectionClass($dataClassString));
$dataClass = $dataClassFactory->build(new ReflectionClass($dataClassString));

$dataClass->prepareForCache();

Expand Down
2 changes: 1 addition & 1 deletion src/DataPipes/CastPropertiesDataPipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ protected function shouldBeCasted(DataProperty $property, mixed $value): bool
return true; // Transform everything to data objects
}

return $property->type->type->acceptsValue($value) === false;
return $property->type->acceptsValue($value) === false;
}
}
2 changes: 1 addition & 1 deletion src/DataPipes/DefaultValuesDataPipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function handle(
return;
}

if ($property->type->isNullable()) {
if ($property->type->isNullable) {
$properties[$property->name] = null;

return;
Expand Down
28 changes: 14 additions & 14 deletions src/Exceptions/CannotFindDataClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
namespace Spatie\LaravelData\Exceptions;

use Exception;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;

class CannotFindDataClass extends Exception
{
public static function noDataReferenceFound(string $class, string $propertyName): self
public static function forTypeable(ReflectionMethod|ReflectionProperty|ReflectionParameter|string $typeable): self
{
return new self("Property `{$propertyName}` in `{$class}` is not a data object or collection");
}
if (is_string($typeable)) {
return new self("Cannot find data class for type `{$typeable}`");
}

public static function missingDataCollectionAnotation(string $class, string $propertyName): self
{
return new self("Data collection property `{$propertyName}` in `{$class}` is missing an annotation with the type of data it represents");
}
$class = $typeable->getDeclaringClass()->getName();

public static function wrongDataCollectionAnnotation(string $class, string $propertyName): self
{
return new self("Data collection property `{$propertyName}` in `{$class}` has an annotation that isn't a data object or is missing an annotation");
}
$name = match (true) {
$typeable instanceof ReflectionMethod => "method `{$class}::{{$typeable->getName()}`",
$typeable instanceof ReflectionProperty => "property `{$class}::{{$typeable->getName()}`",
$typeable instanceof ReflectionParameter => "parameter `{$class}::{$typeable->getDeclaringFunction()->getName()}::{$typeable->getName()}`",
};

public static function cannotReadReflectionParameterDocblock(string $class, string $parameter): self
{
return new self("Data collection reflection parameter `{$parameter}` in `{$class}::__constructor` has an annotation that isn't a data object or is missing an annotation");
return new self("Cannot find data class for {$name}");
}
}
24 changes: 12 additions & 12 deletions src/Resolvers/DataCollectableFromSomethingResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
use Spatie\LaravelData\Support\Creation\CreationContext;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\DataMethod;
use Spatie\LaravelData\Support\Types\PartialType;
use Spatie\LaravelData\Support\Factories\DataReturnTypeFactory;
use Spatie\LaravelData\Support\Factories\DataTypeFactory;

class DataCollectableFromSomethingResolver
{
public function __construct(
protected DataConfig $dataConfig,
protected DataFromSomethingResolver $dataFromSomethingResolver,
protected DataReturnTypeFactory $dataReturnTypeFactory,
) {
}

Expand All @@ -37,30 +39,28 @@ public function execute(
?string $into = null,
): array|DataCollection|PaginatedDataCollection|CursorPaginatedDataCollection|Enumerable|AbstractPaginator|Paginator|AbstractCursorPaginator|CursorPaginator {
$intoType = $into !== null
? PartialType::createFromTypeString($into)
: PartialType::createFromValue($items);
? $this->dataReturnTypeFactory->buildFromNamedType($into)
: $this->dataReturnTypeFactory->buildFromValue($items);

$collectable = $this->createFromCustomCreationMethod($dataClass, $creationContext, $items, $into);

if ($collectable) {
return $collectable;
}

$intoDataTypeKind = $intoType->getDataTypeKind();

$collectableMetaData = CollectableMetaData::fromOther($items);

$normalizedItems = $this->normalizeItems($items, $dataClass, $creationContext);

return match ($intoDataTypeKind) {
return match ($intoType->kind) {
DataTypeKind::Array => $this->normalizeToArray($normalizedItems),
DataTypeKind::Enumerable => new $intoType->name($this->normalizeToArray($normalizedItems)),
DataTypeKind::DataCollection => new $intoType->name($dataClass, $this->normalizeToArray($normalizedItems)),
DataTypeKind::DataPaginatedCollection => new $intoType->name($dataClass, $this->normalizeToPaginator($normalizedItems, $collectableMetaData)),
DataTypeKind::DataCursorPaginatedCollection => new $intoType->name($dataClass, $this->normalizeToCursorPaginator($normalizedItems, $collectableMetaData)),
DataTypeKind::Enumerable => new $intoType->type->name($this->normalizeToArray($normalizedItems)),
DataTypeKind::DataCollection => new $intoType->type->name($dataClass, $this->normalizeToArray($normalizedItems)),
DataTypeKind::DataPaginatedCollection => new $intoType->type->name($dataClass, $this->normalizeToPaginator($normalizedItems, $collectableMetaData)),
DataTypeKind::DataCursorPaginatedCollection => new $intoType->type->name($dataClass, $this->normalizeToCursorPaginator($normalizedItems, $collectableMetaData)),
DataTypeKind::Paginator => $this->normalizeToPaginator($normalizedItems, $collectableMetaData),
DataTypeKind::CursorPaginator => $this->normalizeToCursorPaginator($normalizedItems, $collectableMetaData),
default => CannotCreateDataCollectable::create(get_debug_type($items), $intoType->name)
default => throw CannotCreateDataCollectable::create(get_debug_type($items), $intoType->type->name)
};
}

Expand Down Expand Up @@ -107,7 +107,7 @@ protected function createFromCustomCreationMethod(
$payload = [];

foreach ($method->parameters as $parameter) {
if ($parameter->isCreationContext) {
if ($parameter->type->type->isCreationContext()) {
$payload[$parameter->name] = $creationContext;
} else {
$payload[$parameter->name] = $this->normalizeItems($items, $dataClass, $creationContext);
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/DataFromArrayResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function execute(string $class, Collection $properties): BaseData
}

if ($property->computed
&& $property->type->isNullable()
&& $property->type->isNullable
&& $properties->get($property->name) === null
) {
return; // Nullable properties get assigned null by default
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/DataFromSomethingResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ protected function createFromCustomCreationMethod(
}

foreach ($method->parameters as $index => $parameter) {
if ($parameter->isCreationContext) {
if ($parameter->type->type->isCreationContext()) {
$payloads[$index] = $creationContext;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/DataValidationRulesResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ protected function resolveDataSpecificRules(
DataRules $dataRules,
): void {
$isOptionalAndEmpty = $dataProperty->type->isOptional && Arr::has($fullPayload, $propertyPath->get()) === false;
$isNullableAndEmpty = $dataProperty->type->isNullable() && Arr::get($fullPayload, $propertyPath->get()) === null;
$isNullableAndEmpty = $dataProperty->type->isNullable && Arr::get($fullPayload, $propertyPath->get()) === null;

if ($isOptionalAndEmpty || $isNullableAndEmpty) {
$this->resolveToplevelRules(
Expand Down
7 changes: 4 additions & 3 deletions src/Resolvers/EmptyDataResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Spatie\LaravelData\Exceptions\DataPropertyCanOnlyHaveOneType;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\DataProperty;
use Spatie\LaravelData\Support\Types\MultiType;
use Spatie\LaravelData\Support\Types\CombinationType;
use Traversable;

class EmptyDataResolver
Expand Down Expand Up @@ -37,11 +37,12 @@ public function execute(string $class, array $extra = []): array
protected function getValueForProperty(DataProperty $property): mixed
{
$propertyType = $property->type;
if ($propertyType->isMixed()) {

if ($propertyType->isMixed) {
return null;
}

if ($propertyType->type instanceof MultiType && $propertyType->type->acceptedTypesCount() > 1) {
if ($propertyType->type instanceof CombinationType && count($propertyType->type->types) > 1) {
throw DataPropertyCanOnlyHaveOneType::create($property);
}

Expand Down
2 changes: 1 addition & 1 deletion src/RuleInferrers/NullableRuleInferrer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function handle(
PropertyRules $rules,
ValidationContext $context,
): PropertyRules {
if ($property->type->isNullable() && ! $rules->hasType(Nullable::class)) {
if ($property->type->isNullable && ! $rules->hasType(Nullable::class)) {
$rules->prepend(new Nullable());
}

Expand Down
2 changes: 1 addition & 1 deletion src/RuleInferrers/RequiredRuleInferrer.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function handle(

protected function shouldAddRule(DataProperty $property, PropertyRules $rules): bool
{
if ($property->type->isNullable() || $property->type->isOptional) {
if ($property->type->isNullable || $property->type->isOptional) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Support/Casting/GlobalCastsCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function merge(self $casts): self

public function findCastForValue(DataProperty $property): ?Cast
{
foreach ($property->type->type->getAcceptedTypes() as $acceptedType => $baseTypes) {
foreach ($property->type->getAcceptedTypes() as $acceptedType => $baseTypes) {
foreach ([$acceptedType, ...$baseTypes] as $type) {
if ($cast = $this->casts[$type] ?? null) {
return $cast;
Expand Down
Loading

0 comments on commit 05a1098

Please sign in to comment.