Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge f0691e0 into 753457e
Browse files Browse the repository at this point in the history
  • Loading branch information
tux-rampage committed Aug 29, 2018
2 parents 753457e + f0691e0 commit 9030941
Show file tree
Hide file tree
Showing 21 changed files with 764 additions and 171 deletions.
9 changes: 2 additions & 7 deletions src/CodeGenerator/AbstractInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Zend\Di\CodeGenerator;

use function is_string;
use Psr\Container\ContainerInterface;
use Zend\Di\DefaultContainer;
use Zend\Di\InjectorInterface;
Expand Down Expand Up @@ -49,25 +50,19 @@ abstract protected function loadFactoryList();

private function getFactory($type) : FactoryInterface
{
if (\is_string($this->factories[$type])) {
if (is_string($this->factories[$type])) {
$factory = $this->factories[$type];
$this->factories[$type] = new $factory();
}

return $this->factories[$type];
}

/**
* {@inheritDoc}
*/
public function canCreate(string $name) : bool
{
return (isset($this->factories[$name]) || $this->injector->canCreate($name));
}

/**
* {@inheritDoc}
*/
public function create(string $name, array $options = [])
{
if (isset($this->factories[$name])) {
Expand Down
121 changes: 60 additions & 61 deletions src/CodeGenerator/FactoryGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
use Zend\Code\Generator\FileGenerator;
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\ValueGenerator;
use Zend\Di\ConfigInterface;
use Zend\Di\Resolver\AbstractInjection;
use Zend\Di\Resolver\DependencyResolverInterface;
use Zend\Di\Resolver\InjectionInterface;
use Zend\Di\Resolver\TypeInjection;

/**
Expand All @@ -25,6 +26,18 @@ class FactoryGenerator
{
use GeneratorTrait;

const PARAMETERS_TEMPLATE = <<<__CODE__
if (empty(\$options)) {
\$args = [
%s
];
} else {
\$args = [
%s
];
}
__CODE__;

/**
* @var string
*/
Expand Down Expand Up @@ -58,29 +71,17 @@ public function __construct(
$this->namespace = $namespace ?: 'ZendDiGenerated';
}

/**
* @param string $name
* @return string
*/
protected function buildClassName(string $name)
protected function buildClassName(string $name): string
{
return preg_replace('~[^a-z0-9\\\\]+~i', '_', $name) . 'Factory';
}

/**
* @param string $name
* @return string
*/
protected function buildFileName(string $name)
protected function buildFileName(string $name): string
{
$name = $this->buildClassName($name);
return str_replace('\\', '/', $name) . '.php';
}

/**
* @param string $type
* @return string|unknown
*/
private function getClassName(string $type) : string
{
if ($this->config->isAlias($type)) {
Expand All @@ -90,27 +91,31 @@ private function getClassName(string $type) : string
return $type;
}

/**
* @param InjectionInterface[] $injections
*/
private function canGenerateForParameters(iterable $injections): bool
{
foreach ($injections as $injection) {
if (! $injection->isExportable()) {
return false;
}
}

return true;
}

/**
* Builds the code for constructor parameters
*
* @param string $type The type name to build for
* @param InjectionInterface[] $injections
*/
private function buildParametersCode(string $type)
private function buildParametersCode(iterable $injections): ?string
{
$params = $this->resolver->resolveParameters($type);
$names = [];

$withOptions = [];
$withoutOptions = [];

/** @var AbstractInjection $injection */
foreach ($params as $injection) {
if (! $injection->isExportable()) {
return false;
}

$name = $injection->getParameterName();
$variable = '$p_' . $name;
foreach ($injections as $name => $injection) {
$code = $injection->export();

if ($injection instanceof TypeInjection) {
Expand All @@ -120,32 +125,29 @@ private function buildParametersCode(string $type)
// build for two cases:
// 1. Parameters are passed at call time
// 2. No Parameters were passed at call time (might be slightly faster)
$names[] = $variable;
$withoutOptions[] = sprintf('%s = %s;', $variable, $code);
$withoutOptions[] = sprintf('%s, // %s', $code, $name);
$withOptions[] = sprintf(
'%1$s = array_key_exists(%3$s, $options)? $options[%3$s] : %2$s;',
$variable,
$code,
var_export($name, true)
'array_key_exists(%1$s, $options)? $options[%1$s] : %2$s,',
var_export($name, true),
$code
);
}

$intention = 4;
$tab = str_repeat(' ', $intention);
$code = '';

if ($withOptions) {
// Build conditional initializer code:
// If no $params were provided ignore it completely
// otherwise check if there is a value for each dependency in $params.
$code = 'if (empty($options)) {' . "\n"
. $tab . implode("\n$tab", $withoutOptions) . "\n"
. '} else {' . "\n"
. $tab . implode("\n$tab", $withOptions)
. "\n}\n\n";
if (! $withOptions) {
return null;
}

return [$names, $code];
$intention = 4;
$tab = str_repeat(' ', $intention * 4);

// Build conditional initializer code:
// If no $params were provided ignore it completely
// otherwise check if there is a value for each dependency in $params.
return sprintf(
self::PARAMETERS_TEMPLATE,
implode("\n$tab", $withoutOptions),
implode("\n$tab", $withOptions)
) . "\n\n";
}

/**
Expand All @@ -155,34 +157,31 @@ private function buildParametersCode(string $type)
private function buildCreateMethodBody(string $type)
{
$class = $this->getClassName($type);
$result = $this->buildParametersCode($type);
$injections = $this->resolver->resolveParameters($type);

// The resolver was unable to deliver and somehow the instantiator
// was not considered a requirement. Whatever caused this, it's not acceptable here
if (! $result) {
if (! $this->canGenerateForParameters($injections)) {
return false;
}

list($paramNames, $paramsCode) = $result;

// Decide if new or static method call should be used
$paramsCode = $this->buildParametersCode($injections);
$absoluteClassName = '\\' . $class;
$invokeCode = sprintf('new %s(%s)', $absoluteClassName, implode(', ', $paramNames));
$args = ($paramsCode !== null) ? '...$args' : '';
$invokeCode = sprintf('new %s(%s)', $absoluteClassName, $args);

return $paramsCode . "return $invokeCode;\n";
}

private function buildInvokeMethod(ClassGenerator $generator)
{
$code = 'if (is_string($options)) {' . PHP_EOL
. ' $options = is_array($zfCompatibleOptions) ? $zfCompatibleOptions : [];' . PHP_EOL
$code = 'if (is_array($name) && ($options === null)) {' . PHP_EOL
. ' $options = $name;' . PHP_EOL
. '}' . PHP_EOL . PHP_EOL
. 'return $this->create($container, $options);';
. 'return $this->create($container, $options ?? []);';

$args = [
new ParameterGenerator('container', ContainerInterface::class),
new ParameterGenerator('options', null, []),
new ParameterGenerator('zfCompatibleOptions', null, []),
new ParameterGenerator('name', null, new ValueGenerator(null, ValueGenerator::TYPE_NULL)),
new ParameterGenerator('options', 'array', new ValueGenerator(null, ValueGenerator::TYPE_NULL)),
];

$generator->addMethod('__invoke', $args, MethodGenerator::FLAG_PUBLIC, $code);
Expand Down
59 changes: 25 additions & 34 deletions src/Injector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Zend\Di\Resolver\InjectionInterface;
use Zend\Di\Resolver\TypeInjection;
use function in_array;

/**
* Dependency injector that can generate instances using class definitions and configured instance parameters
Expand Down Expand Up @@ -179,6 +182,27 @@ protected function createInstance(string $name, array $params)
return new $class(...$callParameters);
}

/**
* @return mixed The value to inject into the instance
*/
private function getInjectionValue(InjectionInterface $injection)
{
$container = $this->container;
$containerTypes = [
ContainerInterface::class,
'Interop\Container\ContainerInterface' // Be backwards compatible with interop/container
];

if (($injection instanceof TypeInjection) &&
! $container->has((string)$injection) &&
in_array((string)$injection, $containerTypes, true)
) {
return $container;
}

return $injection->toValue($container);
}

/**
* Resolve parameters
*
Expand All @@ -201,42 +225,9 @@ private function resolveParameters(string $type, array $params = []) : array
{
$resolved = $this->resolver->resolveParameters($type, $params);
$params = [];
$container = $this->container;
$containerTypes = [
ContainerInterface::class,
'Interop\Container\ContainerInterface' // Be backwards compatible with interop/container
];

foreach ($resolved as $position => $injection) {
if ($injection instanceof Resolver\ValueInjection) {
$params[] = $injection->getValue();
continue;
}

if (! $injection instanceof Resolver\TypeInjection) {
throw new Exception\UnexpectedValueException(sprintf(
'Invalid injection type: %s',
is_object($injection) ? get_class($injection) : gettype($injection)
));
}

$type = $injection->getType();

if (! $container->has($type)) {
if (in_array($type, $containerTypes)) {
$params[] = $container;
continue;
}

throw new Exception\UndefinedReferenceException(sprintf(
'Could not obtain instance %s from ioc container for parameter %s of type %s',
$type,
$position,
$type
));
}

$params[] = $container->get($type);
$params[] = $this->getInjectionValue($injection);
}

return $params;
Expand Down
18 changes: 18 additions & 0 deletions src/Resolver/AbstractInjection.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

namespace Zend\Di\Resolver;

use const E_USER_DEPRECATED;
use function trigger_error;

/**
* @codeCoverageIgnore Deprecated
* @deprecated Since 3.1.0
* @see InjectionInterface
*/
abstract class AbstractInjection
{
/**
Expand All @@ -20,6 +28,11 @@ abstract class AbstractInjection
*/
public function setParameterName(string $name) : self
{
trigger_error(
__CLASS__ . ' is deprecated, please migrate to ' . InjectionInterface::class,
E_USER_DEPRECATED
);

$this->parameterName = $name;
return $this;
}
Expand All @@ -29,6 +42,11 @@ public function setParameterName(string $name) : self
*/
public function getParameterName() : string
{
trigger_error(
__CLASS__ . ' is deprecated, please migrate to ' . InjectionInterface::class,
E_USER_DEPRECATED
);

return $this->parameterName;
}

Expand Down
12 changes: 4 additions & 8 deletions src/Resolver/DependencyResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,10 @@ private function isCallableType(string $type): bool
* If the candidate is usable, its injection representation is returned
*
* @param mixed $value
* @param string|null $requiredType
* @return null|TypeInjection|ValueInjection
* @param null|string $requiredType
* @return null|InjectionInterface
*/
private function prepareInjection($value, ?string $requiredType) : ?AbstractInjection
private function prepareInjection($value, ?string $requiredType) : ?InjectionInterface
{
if (($value instanceof ValueInjection) || ($value instanceof TypeInjection)) {
return $value;
Expand Down Expand Up @@ -266,7 +266,7 @@ private function prepareInjection($value, ?string $requiredType) : ?AbstractInje
* @param array $callTimeParameters
* @throws Exception\UnexpectedValueException
* @throws Exception\MissingPropertyException
* @return AbstractInjection[]
* @return InjectionInterface[]
*/
public function resolveParameters(string $requestedType, array $callTimeParameters = []) : array
{
Expand Down Expand Up @@ -337,10 +337,6 @@ public function resolveParameters(string $requestedType, array $callTimeParamete
$result[$name] = new ValueInjection($paramInfo->getDefault());
}

foreach ($result as $name => $injection) {
$injection->setParameterName($name);
}

return $result;
}

Expand Down
Loading

0 comments on commit 9030941

Please sign in to comment.