178 lines
5.9 KiB
PHP
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;
|
|
}
|
|
}
|