parent
4c806c8e16
commit
67d795a506
5 changed files with 184 additions and 0 deletions
@ -0,0 +1,35 @@ |
|||||||
|
--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,51 @@ |
|||||||
|
--TEST-- |
||||||
|
Symfony pattern: class attribute with named arguments and constructor normalization |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] |
||||||
|
final class SymfonyLikeCommand |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
public string $name, |
||||||
|
public ?string $description = null, |
||||||
|
array $aliases = [], |
||||||
|
bool $hidden = false, |
||||||
|
public ?string $help = null, |
||||||
|
public array $usages = [], |
||||||
|
) { |
||||||
|
if (!$hidden && !$aliases) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
$names = explode('|', $name); |
||||||
|
$names = array_merge($names, $aliases); |
||||||
|
|
||||||
|
if ($hidden && '' !== $names[0]) { |
||||||
|
array_unshift($names, ''); |
||||||
|
} |
||||||
|
|
||||||
|
$this->name = implode('|', $names); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[SymfonyLikeCommand(name: 'cache:clear', description: 'Clear cache', hidden: true, help: 'Clears generated cache')] |
||||||
|
class SymfonyLikeClearCacheCommand |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$attribute = (new ReflectionClass(SymfonyLikeClearCacheCommand::class))->getAttributes(SymfonyLikeCommand::class)[0]; |
||||||
|
$command = $attribute->newInstance(); |
||||||
|
|
||||||
|
var_dump($command->name); |
||||||
|
var_dump($command->description); |
||||||
|
var_dump($command->usages); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(12) "|cache:clear" |
||||||
|
string(11) "Clear cache" |
||||||
|
array(0) { |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
--TEST-- |
||||||
|
Symfony pattern: container caches first-class method callable with ??= |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class SymfonyLikeContainer |
||||||
|
{ |
||||||
|
public array $services = []; |
||||||
|
|
||||||
|
public function getService(string $id): string |
||||||
|
{ |
||||||
|
return 'service:'.$id; |
||||||
|
} |
||||||
|
|
||||||
|
public function locator(): array |
||||||
|
{ |
||||||
|
return [ |
||||||
|
$this->getService ??= $this->getService(...), |
||||||
|
['foo', 'bar'], |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$container = new SymfonyLikeContainer(); |
||||||
|
[$factory, $ids] = $container->locator(); |
||||||
|
|
||||||
|
foreach ($ids as $id) { |
||||||
|
var_dump($factory($id)); |
||||||
|
} |
||||||
|
|
||||||
|
var_dump($container->getService instanceof Closure); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(11) "service:foo" |
||||||
|
string(11) "service:bar" |
||||||
|
bool(true) |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
--TEST-- |
||||||
|
Symfony pattern: dynamic method forwarding with unpacked arguments |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class SymfonyLikeInnerSerializer |
||||||
|
{ |
||||||
|
public function format(string $value, string $prefix = '', string $suffix = ''): string |
||||||
|
{ |
||||||
|
return $prefix.strtoupper($value).$suffix; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class SymfonyLikeTraceableSerializer |
||||||
|
{ |
||||||
|
public function __construct(private SymfonyLikeInnerSerializer $serializer) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function __call(string $method, array $arguments): mixed |
||||||
|
{ |
||||||
|
return $this->serializer->{$method}(...$arguments); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$serializer = new SymfonyLikeTraceableSerializer(new SymfonyLikeInnerSerializer()); |
||||||
|
var_dump($serializer->format('symfony', '[', ']')); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(9) "[SYMFONY]" |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
--TEST-- |
||||||
|
Symfony pattern: local variable reassigns from string to array in constructor |
||||||
|
--SKIPIF-- |
||||||
|
<?php |
||||||
|
exit('skip Known AOT compile bug: local variable cannot widen from string to array'); |
||||||
|
?> |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class SymfonyLikeCommandNameNormalizer |
||||||
|
{ |
||||||
|
public function __construct(public string $name, array $aliases = []) |
||||||
|
{ |
||||||
|
$name = explode('|', $name); |
||||||
|
$name = array_merge($name, $aliases); |
||||||
|
$this->name = implode('|', $name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
var_dump((new SymfonyLikeCommandNameNormalizer('cache:clear', ['cache:clean']))->name); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(23) "cache:clear|cache:clean" |
||||||
Loading…
Reference in new issue