Files
kupshop/bundles/KupShop/DevelopmentBundle/Util/Tests/ContainerAwareTestTrait.php
2025-08-02 16:30:27 +02:00

178 lines
5.9 KiB
PHP

<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Contracts\Service\Attribute\Required;
trait ContainerAwareTestTrait
{
/**
* @template T
*
* @param $service string|class-string<T>
*
* @return mixed|T
*/
public function get($service)
{
return ServiceContainer::getService($service);
}
public function set($service, $instance, $container = null)
{
$container = $container ?: ServiceContainer::getContainer();
// osklivost, ale jinak to proste nejde :/ ten container pro testy umoznuje pouze pristupovat k private
// servisam, ale setovani to uz nepodporuje a spadne to napr. na tom, ze se nemuze nasetovat private servica
$refObject = new \ReflectionObject($container);
$refMethod = $refObject->getMethod('getPublicContainer');
$refMethod->setAccessible(true);
$publicContainer = $refMethod->invoke($container);
$refObject = new \ReflectionObject($publicContainer);
// povolim to v syntheticIds - to umozni nasetovat tu servisu do public containeru
$refProperty = $refObject->getProperty('syntheticIds');
$refProperty->setAccessible(true);
$refProperty->setValue($publicContainer, array_merge($refProperty->getValue($publicContainer), [$service => $instance]));
// nasetuju tu servisu i do private containeru
$refProperty = $refObject->getProperty('privates');
$refProperty->setAccessible(true);
$refProperty->setValue($publicContainer, array_merge($refProperty->getValue($publicContainer), [$service => $instance]));
return $publicContainer->set($service, $instance);
}
/**
* Creates a Client.
*
* @param array $options An array of options to pass to the createKernel class
* @param array $server An array of server parameters
*
* @return KernelBrowser A Client instance
*/
protected function createClient(array $options = [], array $server = [])
{
$server = array_merge([
'HTTPS' => true,
'HTTP_HOST' => 'www.kupshop.local',
], $server);
/** @var KernelBrowser $client */
$client = $this->get('test.client');
$client->setServerParameters($server);
$client->disableReboot();
return $client;
}
public function tearDownContainer()
{
// shutdown kernel before being destroyed - to be memory freed correctly
ServiceContainer::getKernel()->shutdown();
// destroy kernel
ServiceContainer::destroy();
}
/**
* Instantiate a $class and autowire it with services from the Service Container.
*
* @template T
*
* @param class-string<T> $class
*
* @return T
*
* @throws \ReflectionException
*/
protected function autowire(string $class)
{
$rc = new \ReflectionClass($class);
$constructor = $rc->getConstructor();
// get constructor arguments from the container
$constructorArgs = [];
if ($constructor) {
foreach ($constructor->getParameters() as $name => $param) {
$classFqn = $param->getType()?->getName();
$constructorArgs[$name] = $this->get($classFqn);
}
}
$instance = $rc->newInstanceArgs($constructorArgs);
// setter autowiring - public and has the `@required` annotation
$setters = array_filter(
$rc->getMethods(\ReflectionMethod::IS_PUBLIC),
self::isRequired(...),
);
foreach ($setters as $setter) {
if ($setter->getNumberOfParameters() !== 1) {
continue;
}
$param = $setter->getParameters()[0];
$classFqn = $param->getType()?->getName();
$setter->invoke($instance, $this->get($classFqn));
}
// autowire public properties with the `@required` annotation or Required attribute
$requiredProperties = array_filter(
$rc->getProperties(\ReflectionProperty::IS_PUBLIC),
self::isRequired(...),
);
foreach ($requiredProperties as $property) {
$classFqn = $property->getType()?->getName();
$instance->{$property->getName()} = $this->get($classFqn);
}
return $instance;
}
protected function getRequiredProperties(object $class): array
{
$reflectionClass = new \ReflectionClass($class);
$requiredProperties = [];
// Check properties
foreach ($reflectionClass->getProperties() as $property) {
$attributes = $property->getAttributes(Required::class);
$requireDoc = str_contains($property->getDocComment() ?? '', '@required');
if (!empty($attributes) || !empty($requireDoc)) {
$requiredProperties[] = $property->getName();
}
}
// Check methods
foreach ($reflectionClass->getMethods() as $method) {
$attributes = $method->getAttributes(Required::class);
$requireDoc = str_contains($method->getDocComment() ?? '', '@required');
if (!empty($attributes) || !empty($requireDoc)) {
// Check if the method is a setter
if (preg_match('/^set(.+)$/', $method->getName(), $matches)) {
$propertyName = lcfirst($matches[1]);
$requiredProperties[] = $propertyName;
}
}
}
return $requiredProperties;
}
private static function isRequired(\ReflectionMethod|\ReflectionProperty $prop): bool
{
$requireAttr = !empty($prop->getAttributes(Required::class));
$requireDoc = str_contains($prop->getDocComment() ?? '', '@required');
return $requireDoc || $requireAttr;
}
}