Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
478 changes: 478 additions & 0 deletions docs/plans/2026-09-04-0853-data-automatic-lean-execution.md

Large diffs are not rendered by default.

25 changes: 2 additions & 23 deletions src/data/src/Casts/BuiltinTypeCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Hypervel\Data\Support\Creation\ConstructionState;
use Hypervel\Data\Support\Creation\CreationContext;
use Hypervel\Data\Support\Creation\ValueCaster;
use Hypervel\Data\Support\DataProperty;

class BuiltinTypeCast implements Cast, IterableItemCast
Expand Down Expand Up @@ -49,28 +50,6 @@ public function castIterableItem(
*/
protected function runCast(mixed $value): mixed
{
return match ($this->type) {
'bool' => $this->castToBool($value),
'int' => (int) $value,
'float' => (float) $value,
'array' => (array) $value,
'string' => (string) $value,
};
}

/**
* Cast one value to a boolean.
*/
protected function castToBool(mixed $value): bool
{
if (! is_string($value)) {
return (bool) $value;
}

return match (strtolower($value)) {
'true' => true,
'false' => false,
default => (bool) $value,
};
return ValueCaster::castBuiltin($this->type, $value);
}
}
77 changes: 9 additions & 68 deletions src/data/src/Casts/DateTimeInterfaceCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@

namespace Hypervel\Data\Casts;

use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Hypervel\Data\Exceptions\CannotCastDate;
use Hypervel\Data\Support\Creation\ConstructionState;
use Hypervel\Data\Support\Creation\CreationContext;
use Hypervel\Data\Support\Creation\ValueCaster;
use Hypervel\Data\Support\DataProperty;
use Hypervel\Support\Facades\Date;
use Throwable;

class DateTimeInterfaceCast implements Cast, IterableItemCast
{
Expand Down Expand Up @@ -73,68 +68,14 @@ protected function castValue(
mixed $value,
CreationContext $context,
): Uncastable|DateTimeInterface {
if ($type === null) {
return Uncastable::create();
}

$formats = $this->format === null
? $context->dateFormats
: (is_array($this->format) ? $this->format : [$this->format]);

if (is_string($value)) {
$value = preg_replace('/(\.\d{6})\d+/', '$1', $value);
}

$sourceTimeZone = $this->timeZone === null ? null : new DateTimeZone($this->timeZone);

foreach ($formats as $format) {
try {
$datetime = $this->createDate(
$type,
$format,
$value instanceof DateTimeInterface ? $value->format($format) : (string) $value,
$sourceTimeZone,
);
} catch (Throwable) {
$datetime = null;
}

if ($datetime === null) {
continue;
}

$targetTimeZone = $this->setTimeZone ?? $context->dateTimezone;

return $targetTimeZone === null
? $datetime
: $datetime->setTimezone(new DateTimeZone($targetTimeZone));
}

throw CannotCastDate::create($formats, $type, $value);
}

/**
* Create a date using the declared concrete type or Hypervel's date factory.
*
* @param class-string<DateTimeInterface> $type
*/
protected function createDate(
string $type,
string $format,
string $value,
?DateTimeZone $timeZone,
): DateTime|DateTimeImmutable|null {
$datetime = is_a($type, DateTime::class, true) || is_a($type, DateTimeImmutable::class, true)
? $type::createFromFormat($format, $value, $timeZone)
: Date::createFromFormat($format, $value, $timeZone);

if ((! $datetime instanceof DateTime && ! $datetime instanceof DateTimeImmutable)
|| ! $datetime instanceof $type
) {
return null;
}

return $datetime;
return ValueCaster::castDate(
type: $type,
value: $value,
context: $context,
format: $this->format,
setTimeZone: $this->setTimeZone,
timeZone: $this->timeZone,
);
}

/**
Expand Down
23 changes: 2 additions & 21 deletions src/data/src/Casts/EnumCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
namespace Hypervel\Data\Casts;

use BackedEnum;
use Hypervel\Data\Exceptions\CannotCastEnum;
use Hypervel\Data\Support\Creation\ConstructionState;
use Hypervel\Data\Support\Creation\CreationContext;
use Hypervel\Data\Support\Creation\ValueCaster;
use Hypervel\Data\Support\DataProperty;
use Throwable;

use function Hypervel\Support\enum_from;

class EnumCast implements Cast, IterableItemCast
{
Expand Down Expand Up @@ -67,23 +64,7 @@ protected function castValue(
mixed $value,
DataProperty $property,
): BackedEnum|Uncastable {
if ($type === null) {
return Uncastable::create();
}

if ($value instanceof $type) {
return $value;
}

if ($value instanceof BackedEnum) {
$value = $value->value;
}

try {
return enum_from($type, $value);
} catch (Throwable) {
throw CannotCastEnum::create($type, $value, $property);
}
return ValueCaster::castEnum($type, $value, $property);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/data/src/DataServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

namespace Hypervel\Data;

use Hypervel\Contracts\Foundation\Application;
use Hypervel\Data\Console\DataMakeCommand;
use Hypervel\Data\Contracts\TransformableData;
use Hypervel\Data\Support\Creation\DataCreator;
use Hypervel\Data\Support\DataConfig;
use Hypervel\Data\Support\Transformation\DataTransformer;
use Hypervel\Data\Support\VarDumper\DataVarDumperCaster;
use Hypervel\Support\ServiceProvider;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
Expand Down Expand Up @@ -35,6 +38,13 @@ public function boot(): void
// Build the typed configuration once during worker boot.
$this->app->make(DataConfig::class);

if (! $this->app->runningUnitTests()) {
$this->app->booted(static function (Application $app): void {
$app->make(DataCreator::class);
$app->make(DataTransformer::class);
});
}

AbstractCloner::$defaultCasters[TransformableData::class]
??= [DataVarDumperCaster::class, 'cast'];

Expand Down
14 changes: 14 additions & 0 deletions src/data/src/Enums/DataPropertyOperation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Hypervel\Data\Enums;

enum DataPropertyOperation
{
case Copy;
case Builtin;
case Enum;
case Date;
case Data;
}
32 changes: 14 additions & 18 deletions src/data/src/Support/Annotations/DataIterableAnnotationReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,9 @@

class DataIterableAnnotationReader
{
protected readonly Lexer $lexer;
protected ?Lexer $lexer = null;

protected readonly PhpDocParser $parser;

/**
* Create a new iterable annotation reader.
*/
public function __construct()
{
$config = new ParserConfig(usedAttributes: []);
$constantExpressionParser = new ConstExprParser($config);

$this->lexer = new Lexer($config);
$this->parser = new PhpDocParser(
$config,
new TypeParser($config, $constantExpressionParser),
$constantExpressionParser,
);
}
protected ?PhpDocParser $parser = null;

/**
* Get iterable annotations declared for class properties.
Expand Down Expand Up @@ -125,6 +109,18 @@ protected function parse(string|false $comment): ?PhpDocNode
return null;
}

if ($this->lexer === null || $this->parser === null) {
$config = new ParserConfig(usedAttributes: []);
$constantExpressionParser = new ConstExprParser($config);

$this->lexer = new Lexer($config);
$this->parser = new PhpDocParser(
$config,
new TypeParser($config, $constantExpressionParser),
$constantExpressionParser,
);
}

return $this->parser->parse(new TokenIterator($this->lexer->tokenize($comment)));
}

Expand Down
Loading