/** * Generates list of arguments using autowiring. * @return array */ public static function autowireArguments(\ReflectionFunctionAbstract $method, array $arguments, $container) { $optCount = 0; $num = -1; $res = array(); $methodName = ($method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' : '') . $method->getName() . '()'; foreach ($method->getParameters() as $num => $parameter) { if (array_key_exists($num, $arguments)) { $res[$num] = $arguments[$num]; unset($arguments[$num]); $optCount = 0; } elseif (array_key_exists($parameter->getName(), $arguments)) { $res[$num] = $arguments[$parameter->getName()]; unset($arguments[$parameter->getName()]); $optCount = 0; } elseif (($class = PhpReflection::getParameterType($parameter)) && !PhpReflection::isBuiltinType($class)) { $res[$num] = $container->getByType($class, FALSE); if ($res[$num] === NULL) { if ($parameter->allowsNull()) { $optCount++; } elseif (class_exists($class) || interface_exists($class)) { $rc = new \ReflectionClass($class); if ($class !== ($hint = $rc->getName())) { throw new ServiceCreationException("Service of type {$class} needed by {$methodName} not found, did you mean {$hint}?"); } throw new ServiceCreationException("Service of type {$class} needed by {$methodName} not found. Did you register it in configuration file?"); } else { throw new ServiceCreationException("Class {$class} needed by {$methodName} not found. Check type hint and 'use' statements."); } } else { if ($container instanceof ContainerBuilder) { $res[$num] = '@' . $res[$num]; } $optCount = 0; } } elseif ($parameter->isOptional() || $parameter->isDefaultValueAvailable()) { // !optional + defaultAvailable = func($a = NULL, $b) since 5.3.17 & 5.4.7 // optional + !defaultAvailable = i.e. Exception::__construct, mysqli::mysqli, ... $res[$num] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL; $optCount++; } else { throw new ServiceCreationException("Parameter \${$parameter->getName()} in {$methodName} has no class type hint or default value, so its value must be specified."); } } // extra parameters while (array_key_exists(++$num, $arguments)) { $res[$num] = $arguments[$num]; unset($arguments[$num]); $optCount = 0; } if ($arguments) { throw new ServiceCreationException("Unable to pass specified arguments to {$methodName}."); } return $optCount ? array_slice($res, 0, -$optCount) : $res; }
private static function mockInjectedProperties() { /** @var \ReflectionProperty $property */ foreach (self::$reflectedClass->getProperties() as $property) { if (Nette\DI\PhpReflection::parseAnnotation($property, 'inject') !== NULL || Nette\DI\PhpReflection::parseAnnotation($property, 'autowire') !== NULL) { if ($mockedParameterClass = Nette\DI\PhpReflection::parseAnnotation($property, 'var')) { $mockedParameterClass = Nette\DI\PhpReflection::expandClassName($mockedParameterClass, Nette\DI\PhpReflection::getDeclaringClass($property)); } self::setProperty($mockedParameterClass, $property); } } }
/** @internal */ private static function checkType($class, string $name, $type, $container) { $rc = PhpReflection::getDeclaringClass(new \ReflectionProperty($class, $name)); $fullname = $rc->getName() . '::$' . $name; if (!$type) { throw new Nette\InvalidStateException("Property {$fullname} has no @var annotation."); } elseif (!class_exists($type) && !interface_exists($type)) { throw new Nette\InvalidStateException("Class or interface '{$type}' used in @var annotation at {$fullname} not found. Check annotation and 'use' statements."); } elseif (!$container->getByType($type, FALSE)) { throw new Nette\InvalidStateException("Service of type {$type} used in @var annotation at {$fullname} not found. Did you register it in configuration file?"); } }
/** @return string|NULL */ private function resolveEntityClass($entity, $recursive = []) { $entity = $this->normalizeEntity($entity instanceof Statement ? $entity->getEntity() : $entity); if (is_array($entity)) { if (($service = $this->getServiceName($entity[0])) || $entity[0] instanceof Statement) { $entity[0] = $this->resolveEntityClass($entity[0], $recursive); if (!$entity[0]) { return; } elseif (isset($this->definitions[$service]) && $this->definitions[$service]->getImplement()) { // @Implement::create return $entity[1] === 'create' ? $this->resolveServiceClass($service, $recursive) : NULL; } } try { $reflection = Nette\Utils\Callback::toReflection($entity[0] === '' ? $entity[1] : $entity); $refClass = $reflection instanceof \ReflectionMethod ? $reflection->getDeclaringClass() : NULL; } catch (\ReflectionException $e) { } if (isset($e) || $refClass && (!$reflection->isPublic() || $refClass->isTrait() && !$reflection->isStatic())) { $name = array_slice(array_keys($recursive), -1); throw new ServiceCreationException(sprintf("Factory '%s' used in service '%s' is not callable.", Nette\Utils\Callback::toString($entity), $name[0])); } return PhpReflection::getReturnType($reflection); } elseif ($service = $this->getServiceName($entity)) { // alias or factory if (Strings::contains($service, '\\')) { // @\Class return ltrim($service, '\\'); } return $this->definitions[$service]->getImplement() ?: $this->resolveServiceClass($service, $recursive); } elseif (is_string($entity)) { if (!class_exists($entity) || !(new ReflectionClass($entity))->isInstantiable()) { $name = array_slice(array_keys($recursive), -1); throw new ServiceCreationException("Class {$entity} used in service '{$name['0']}' not found or is not instantiable."); } return ltrim($entity, '\\'); } }
/** * Generates list of properties with annotation @inject. * @return array */ public static function getInjectProperties(\ReflectionClass $class, $container = NULL) { $res = array(); foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $type = PhpReflection::parseAnnotation($property, 'var'); if (PhpReflection::parseAnnotation($property, 'inject') === NULL) { continue; } elseif (!$type) { throw new Nette\InvalidStateException("Property {$property} has no @var annotation."); } $type = PhpReflection::expandClassName($type, PhpReflection::getDeclaringClass($property)); if (!class_exists($type) && !interface_exists($type)) { throw new Nette\InvalidStateException("Class or interface '{$type}' used in @var annotation at {$property} not found. Check annotation and 'use' statements."); } elseif ($container && !$container->getByType($type, FALSE)) { throw new ServiceCreationException("Service of type {$type} used in @var annotation at {$property} not found. Did you register it in configuration file?"); } $res[$property->getName()] = $type; } return $res; }
/** privates */ private function autowireProps() { $rc = $this->getReflection(); foreach ($rc->getProperties() as $property) { $annotation = $property->getAnnotation('prop'); if ($annotation) { $type = (string) $property->getAnnotation('var'); $propName = isset($annotation['name']) ? $annotation['name'] : $property->name; //todo: implement @prop(type=specifictype) $value = $this->props->get($propName); if (in_array($type, ['array', 'int', 'numeric', 'string'])) { if (!Validators::is($value, $type)) { throw new PropValidationException("Prop {$propName} is not of required type {$type}but " . $this->getObjectType($value)); } } else { $type = PhpReflection::expandClassName($type, $rc); if (!$value instanceof $type) { throw new PropValidationException("Prop {$propName} is not of required type {$type}, but " . $this->getObjectType($value)); } } $this->{$property->name} = $value; } } }
private static function hashParameters(\ReflectionFunctionAbstract $method) { $res = []; if (PHP_VERSION_ID < 70000 && $method->getNumberOfParameters() && $method->getFileName()) { $res[] = file($method->getFileName())[$method->getStartLine() - 1]; } foreach ($method->getParameters() as $param) { $res[] = [$param->getName(), PHP_VERSION_ID >= 70000 ? PhpReflection::getParameterType($param) : NULL, $param->isVariadic(), $param->isDefaultValueAvailable() ? $param->isDefaultValueConstant() ? $param->getDefaultValueConstantName() : [$param->getDefaultValue()] : NULL]; } return $res; }