parent
4fc0acc47f
commit
7d20c4e994
13 changed files with 368 additions and 36 deletions
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)] |
||||
final class PreprocessorAttributeArrayArgument |
||||
{ |
||||
public function __construct(public array $methods = []) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
#[PreprocessorAttributeArrayArgument(methods: ['GET', 'POST'])] |
||||
class PreprocessorAttributeArrayArgumentController |
||||
{ |
||||
} |
||||
@ -0,0 +1,27 @@ |
||||
<?php |
||||
|
||||
#[Attribute(Attribute::TARGET_METHOD)] |
||||
class PreprocessorAttributeSubscribedService |
||||
{ |
||||
public function __construct( |
||||
public string $key, |
||||
public ?PreprocessorAttributeRequired $attribute = null, |
||||
) { |
||||
} |
||||
} |
||||
|
||||
class PreprocessorAttributeRequired |
||||
{ |
||||
public function __construct(public bool $enabled = true) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class PreprocessorAttributeSubscriber |
||||
{ |
||||
#[PreprocessorAttributeSubscribedService(key: 'logger', attribute: new PreprocessorAttributeRequired(false))] |
||||
public function logger(): string |
||||
{ |
||||
return 'logger'; |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@ |
||||
--TEST-- |
||||
Symfony pattern: anonymous exception class implements marker interface |
||||
--FILE-- |
||||
<?php |
||||
|
||||
interface SymfonyLikeNotFoundException |
||||
{ |
||||
} |
||||
|
||||
function createNotFoundException(string $id): Throwable |
||||
{ |
||||
return new class(sprintf('Service "%s" not found.', $id)) extends InvalidArgumentException implements SymfonyLikeNotFoundException { |
||||
}; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$exception = createNotFoundException('mailer'); |
||||
|
||||
var_dump($exception instanceof InvalidArgumentException); |
||||
var_dump($exception instanceof SymfonyLikeNotFoundException); |
||||
var_dump($exception->getMessage()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
string(27) "Service "mailer" not found." |
||||
@ -0,0 +1,56 @@ |
||||
--TEST-- |
||||
Symfony pattern: array_map with first-class object method callable |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class SymfonyLikeParameter |
||||
{ |
||||
public function __construct(private string $name, private int $position) |
||||
{ |
||||
} |
||||
|
||||
public function toArray(): array |
||||
{ |
||||
return [$this->name, $this->position]; |
||||
} |
||||
} |
||||
|
||||
class SymfonyLikeParameterNormalizer |
||||
{ |
||||
public function normalize(SymfonyLikeParameter $parameter): string |
||||
{ |
||||
[$name, $position] = $parameter->toArray(); |
||||
|
||||
return $position.':'.$name; |
||||
} |
||||
} |
||||
|
||||
class SymfonyLikeDescriptor |
||||
{ |
||||
public function __construct(private SymfonyLikeParameterNormalizer $normalizer) |
||||
{ |
||||
} |
||||
|
||||
public function describe(array $parameters): array |
||||
{ |
||||
return array_map($this->normalizer->normalize(...), $parameters); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$descriptor = new SymfonyLikeDescriptor(new SymfonyLikeParameterNormalizer()); |
||||
|
||||
var_dump($descriptor->describe([ |
||||
new SymfonyLikeParameter('request', 0), |
||||
new SymfonyLikeParameter('format', 1), |
||||
])); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(2) { |
||||
[0]=> |
||||
string(9) "0:request" |
||||
[1]=> |
||||
string(8) "1:format" |
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
--TEST-- |
||||
Symfony pattern: array_map with first-class static method callable |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class SymfonyLikeScheduleRenderer |
||||
{ |
||||
public static function render(string $message, DateTimeImmutable $date, bool $all): ?array |
||||
{ |
||||
if (!$all && str_starts_with($message, 'skip')) { |
||||
return null; |
||||
} |
||||
|
||||
return [$message, $date->format('Y-m-d'), $all]; |
||||
} |
||||
|
||||
public static function renderAll(array $messages, DateTimeImmutable $date, bool $all): array |
||||
{ |
||||
return array_filter(array_map( |
||||
self::render(...), |
||||
$messages, |
||||
array_fill(0, count($messages), $date), |
||||
array_fill(0, count($messages), $all) |
||||
)); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
var_dump(SymfonyLikeScheduleRenderer::renderAll(['first', 'skip-second'], new DateTimeImmutable('2026-07-07'), false)); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(1) { |
||||
[0]=> |
||||
array(3) { |
||||
[0]=> |
||||
string(5) "first" |
||||
[1]=> |
||||
string(10) "2026-07-07" |
||||
[2]=> |
||||
bool(false) |
||||
} |
||||
} |
||||
@ -1,35 +0,0 @@ |
||||
--TEST-- |
||||
Symfony pattern: attribute array arguments |
||||
--SKIPIF-- |
||||
<?php |
||||
exit('skip Array arguments to attributes are not supported by the AOT compiler'); |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)] |
||||
final class SymfonyLikeRoute |
||||
{ |
||||
public function __construct(public array $methods = []) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
#[SymfonyLikeRoute(methods: ['GET', 'POST'])] |
||||
class SymfonyLikeController |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$route = (new ReflectionClass(SymfonyLikeController::class))->getAttributes(SymfonyLikeRoute::class)[0]->newInstance(); |
||||
var_dump($route->methods); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(2) { |
||||
[0]=> |
||||
string(3) "GET" |
||||
[1]=> |
||||
string(4) "POST" |
||||
} |
||||
@ -0,0 +1,35 @@ |
||||
--TEST-- |
||||
Symfony pattern: attribute named arguments with class constant values |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)] |
||||
class SymfonyLikeAlias |
||||
{ |
||||
public function __construct( |
||||
public string $id, |
||||
public ?string $when = null, |
||||
) { |
||||
} |
||||
} |
||||
|
||||
interface SymfonyLikeContract |
||||
{ |
||||
} |
||||
|
||||
#[SymfonyLikeAlias(id: SymfonyLikeContract::class, when: 'dev')] |
||||
class SymfonyLikeImplementation implements SymfonyLikeContract |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$attribute = (new ReflectionClass(SymfonyLikeImplementation::class))->getAttributes(SymfonyLikeAlias::class)[0]->newInstance(); |
||||
|
||||
var_dump($attribute->id); |
||||
var_dump($attribute->when); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(19) "SymfonyLikeContract" |
||||
string(3) "dev" |
||||
@ -0,0 +1,40 @@ |
||||
--TEST-- |
||||
Symfony pattern: filter result with coalesce throw expression |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class SymfonyLikeParameterBag |
||||
{ |
||||
public function __construct(private array $parameters) |
||||
{ |
||||
} |
||||
|
||||
public function filter(string $key, mixed $default, int $filter, array $options): mixed |
||||
{ |
||||
return filter_var($this->parameters[$key] ?? $default, $filter, $options); |
||||
} |
||||
|
||||
public function getInt(string $key, int $default = 0): int |
||||
{ |
||||
return $this->filter($key, $default, FILTER_VALIDATE_INT, ['flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE]) |
||||
?? throw new UnexpectedValueException(sprintf('Parameter value "%s" cannot be converted to "int".', $key)); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$bag = new SymfonyLikeParameterBag(['limit' => '42', 'bad' => 'nope']); |
||||
var_dump($bag->getInt('limit')); |
||||
|
||||
try { |
||||
$bag->getInt('bad'); |
||||
} catch (Throwable $e) { |
||||
var_dump($e::class); |
||||
var_dump($e->getMessage()); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(42) |
||||
string(24) "UnexpectedValueException" |
||||
string(51) "Parameter value "bad" cannot be converted to "int"." |
||||
@ -0,0 +1,43 @@ |
||||
--TEST-- |
||||
Symfony pattern: match array filtered then unioned with base arguments |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function schedulerArgs(array $tagAttributes, string $serviceId): array |
||||
{ |
||||
return [ |
||||
'$message' => $serviceId, |
||||
] + array_filter(match ($tagAttributes['trigger'] ?? throw new InvalidArgumentException(sprintf('missing trigger for "%s"', $serviceId))) { |
||||
'every' => [ |
||||
'$frequency' => $tagAttributes['frequency'] ?? throw new InvalidArgumentException(sprintf('missing frequency for "%s"', $serviceId)), |
||||
'$from' => $tagAttributes['from'] ?? null, |
||||
'$until' => $tagAttributes['until'] ?? null, |
||||
], |
||||
'cron' => [ |
||||
'$expression' => $tagAttributes['expression'] ?? throw new InvalidArgumentException(sprintf('missing expression for "%s"', $serviceId)), |
||||
'$timezone' => $tagAttributes['timezone'] ?? null, |
||||
], |
||||
}, static fn ($value) => null !== $value); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
var_dump(schedulerArgs(['trigger' => 'every', 'frequency' => '1 hour', 'from' => null], 'task.one')); |
||||
var_dump(schedulerArgs(['trigger' => 'cron', 'expression' => '* * * * *', 'timezone' => 'UTC'], 'task.two')); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(2) { |
||||
["$message"]=> |
||||
string(8) "task.one" |
||||
["$frequency"]=> |
||||
string(6) "1 hour" |
||||
} |
||||
array(3) { |
||||
["$message"]=> |
||||
string(8) "task.two" |
||||
["$expression"]=> |
||||
string(9) "* * * * *" |
||||
["$timezone"]=> |
||||
string(3) "UTC" |
||||
} |
||||
@ -0,0 +1,41 @@ |
||||
--TEST-- |
||||
Symfony pattern: uasort with spaceship and elvis fallback comparison |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class SymfonyLikeAcceptItem |
||||
{ |
||||
public function __construct(private float $quality, private int $index, public string $name) |
||||
{ |
||||
} |
||||
|
||||
public function getQuality(): float |
||||
{ |
||||
return $this->quality; |
||||
} |
||||
|
||||
public function getIndex(): int |
||||
{ |
||||
return $this->index; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$items = [ |
||||
'json' => new SymfonyLikeAcceptItem(0.9, 2, 'json'), |
||||
'html' => new SymfonyLikeAcceptItem(1.0, 1, 'html'), |
||||
'xml' => new SymfonyLikeAcceptItem(0.9, 0, 'xml'), |
||||
]; |
||||
|
||||
uasort($items, static fn ($a, $b) => $b->getQuality() <=> $a->getQuality() ?: $a->getIndex() <=> $b->getIndex()); |
||||
|
||||
foreach ($items as $item) { |
||||
var_dump($item->name); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(4) "html" |
||||
string(3) "xml" |
||||
string(4) "json" |
||||
Loading…
Reference in new issue