* * @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 $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; } }