| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* This file is part of the Nette Framework (https://nette.org) |
| 5 |
* Copyright (c) 2004 David Grudl (https://davidgrudl.com) |
| 6 |
*/ |
| 7 |
declare (strict_types=1); |
| 8 |
namespace Packetery\Nette\DI\Extensions; |
| 9 |
|
| 10 |
use Packetery\Nette; |
| 11 |
use Packetery\Nette\DI\Config\Helpers; |
| 12 |
use Packetery\Nette\DI\Definitions; |
| 13 |
use Packetery\Nette\DI\Definitions\Statement; |
| 14 |
use Packetery\Nette\Schema\Context; |
| 15 |
use Packetery\Nette\Schema\Expect; |
| 16 |
use Packetery\Nette\Schema\Schema; |
| 17 |
/** |
| 18 |
* Service configuration schema. |
| 19 |
*/ |
| 20 |
class DefinitionSchema implements Schema |
| 21 |
{ |
| 22 |
use \Packetery\Nette\SmartObject; |
| 23 |
/** @var \Packetery\Nette\DI\ContainerBuilder */ |
| 24 |
private $builder; |
| 25 |
public function __construct(\Packetery\Nette\DI\ContainerBuilder $builder) |
| 26 |
{ |
| 27 |
$this->builder = $builder; |
| 28 |
} |
| 29 |
public function complete($def, Context $context) |
| 30 |
{ |
| 31 |
if ($def === [\false]) { |
| 32 |
return (object) $def; |
| 33 |
} |
| 34 |
if (Helpers::takeParent($def)) { |
| 35 |
$def['reset']['all'] = \true; |
| 36 |
} |
| 37 |
foreach (['arguments', 'setup', 'tags'] as $k) { |
| 38 |
if (isset($def[$k]) && Helpers::takeParent($def[$k])) { |
| 39 |
$def['reset'][$k] = \true; |
| 40 |
} |
| 41 |
} |
| 42 |
$def = $this->expandParameters($def); |
| 43 |
$type = $this->sniffType(\end($context->path), $def); |
| 44 |
$def = $this->getSchema($type)->complete($def, $context); |
| 45 |
if ($def) { |
| 46 |
$def->defType = $type; |
| 47 |
} |
| 48 |
return $def; |
| 49 |
} |
| 50 |
public function merge($def, $base) |
| 51 |
{ |
| 52 |
if (!empty($def['alteration'])) { |
| 53 |
unset($def['alteration']); |
| 54 |
} |
| 55 |
return \Packetery\Nette\Schema\Helpers::merge($def, $base); |
| 56 |
} |
| 57 |
/** |
| 58 |
* Normalizes configuration of service definitions. |
| 59 |
*/ |
| 60 |
public function normalize($def, Context $context) |
| 61 |
{ |
| 62 |
if ($def === null || $def === \false) { |
| 63 |
return (array) $def; |
| 64 |
} elseif (\is_string($def) && \interface_exists($def)) { |
| 65 |
return ['implement' => $def]; |
| 66 |
} elseif ($def instanceof Statement && \is_string($def->getEntity()) && \interface_exists($def->getEntity())) { |
| 67 |
$res = ['implement' => $def->getEntity()]; |
| 68 |
if (\array_keys($def->arguments) === ['tagged']) { |
| 69 |
$res += $def->arguments; |
| 70 |
} elseif (\array_keys($def->arguments) === [0]) { |
| 71 |
$res['create'] = $def->arguments[0]; |
| 72 |
} elseif ($def->arguments) { |
| 73 |
$res['references'] = $def->arguments; |
| 74 |
} |
| 75 |
return $res; |
| 76 |
} elseif (!\is_array($def) || isset($def[0], $def[1])) { |
| 77 |
return ['create' => $def]; |
| 78 |
} else { |
| 79 |
// back compatibility |
| 80 |
if (isset($def['factory']) && !isset($def['create'])) { |
| 81 |
$def['create'] = $def['factory']; |
| 82 |
unset($def['factory']); |
| 83 |
} |
| 84 |
if (isset($def['class']) && !isset($def['type']) && !isset($def['dynamic']) && !isset($def['imported'])) { |
| 85 |
$def[isset($def['create']) ? 'type' : 'create'] = $def['class']; |
| 86 |
unset($def['class']); |
| 87 |
} |
| 88 |
foreach (['class' => 'type', 'dynamic' => 'imported'] as $alias => $original) { |
| 89 |
if (\array_key_exists($alias, $def)) { |
| 90 |
if (\array_key_exists($original, $def)) { |
| 91 |
throw new \Packetery\Nette\DI\InvalidConfigurationException(\sprintf("Options '%s' and '%s' are aliases, use only '%s'.", $alias, $original, $original)); |
| 92 |
} |
| 93 |
\trigger_error(\sprintf("Service '%s': option '{$alias}' should be changed to '{$original}'.", \end($context->path)), \E_USER_DEPRECATED); |
| 94 |
$def[$original] = $def[$alias]; |
| 95 |
unset($def[$alias]); |
| 96 |
} |
| 97 |
} |
| 98 |
return $def; |
| 99 |
} |
| 100 |
} |
| 101 |
public function completeDefault(Context $context) |
| 102 |
{ |
| 103 |
} |
| 104 |
private function sniffType($key, array $def) : string |
| 105 |
{ |
| 106 |
if (\is_string($key)) { |
| 107 |
$name = \preg_match('#^@[\\w\\\\]+$#D', $key) ? $this->builder->getByType(\substr($key, 1)) : $key; |
| 108 |
if ($name && $this->builder->hasDefinition($name)) { |
| 109 |
return \get_class($this->builder->getDefinition($name)); |
| 110 |
} |
| 111 |
} |
| 112 |
if (isset($def['implement'], $def['references']) || isset($def['implement'], $def['tagged'])) { |
| 113 |
return Definitions\LocatorDefinition::class; |
| 114 |
} elseif (isset($def['implement'])) { |
| 115 |
return \method_exists($def['implement'], 'create') ? Definitions\FactoryDefinition::class : Definitions\AccessorDefinition::class; |
| 116 |
} elseif (isset($def['imported'])) { |
| 117 |
return Definitions\ImportedDefinition::class; |
| 118 |
} elseif (!$def) { |
| 119 |
throw new \Packetery\Nette\DI\InvalidConfigurationException("Service '{$key}': Empty definition."); |
| 120 |
} else { |
| 121 |
return Definitions\ServiceDefinition::class; |
| 122 |
} |
| 123 |
} |
| 124 |
private function expandParameters(array $config) : array |
| 125 |
{ |
| 126 |
$params = $this->builder->parameters; |
| 127 |
if (isset($config['parameters'])) { |
| 128 |
foreach ((array) $config['parameters'] as $k => $v) { |
| 129 |
$v = \explode(' ', \is_int($k) ? $v : $k); |
| 130 |
$params[\end($v)] = $this->builder::literal('$' . \end($v)); |
| 131 |
} |
| 132 |
} |
| 133 |
return \Packetery\Nette\DI\Helpers::expand($config, $params); |
| 134 |
} |
| 135 |
private static function getSchema(string $type) : Schema |
| 136 |
{ |
| 137 |
static $cache; |
| 138 |
$cache = $cache ?: [Definitions\ServiceDefinition::class => self::getServiceSchema(), Definitions\AccessorDefinition::class => self::getAccessorSchema(), Definitions\FactoryDefinition::class => self::getFactorySchema(), Definitions\LocatorDefinition::class => self::getLocatorSchema(), Definitions\ImportedDefinition::class => self::getImportedSchema()]; |
| 139 |
return $cache[$type]; |
| 140 |
} |
| 141 |
private static function getServiceSchema() : Schema |
| 142 |
{ |
| 143 |
return Expect::structure(['type' => Expect::type('string'), 'create' => Expect::type('callable|\\Packetery\\Nette\\DI\\Definitions\\Statement'), 'arguments' => Expect::array(), 'setup' => Expect::listOf('callable|\\Packetery\\Nette\\DI\\Definitions\\Statement|array:1'), 'inject' => Expect::bool(), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array(), 'reset' => Expect::array(), 'alteration' => Expect::bool()]); |
| 144 |
} |
| 145 |
private static function getAccessorSchema() : Schema |
| 146 |
{ |
| 147 |
return Expect::structure(['type' => Expect::string(), 'implement' => Expect::string(), 'create' => Expect::type('callable|\\Packetery\\Nette\\DI\\Definitions\\Statement'), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array()]); |
| 148 |
} |
| 149 |
private static function getFactorySchema() : Schema |
| 150 |
{ |
| 151 |
return Expect::structure(['type' => Expect::string(), 'create' => Expect::type('callable|\\Packetery\\Nette\\DI\\Definitions\\Statement'), 'implement' => Expect::string(), 'arguments' => Expect::array(), 'setup' => Expect::listOf('callable|\\Packetery\\Nette\\DI\\Definitions\\Statement|array:1'), 'parameters' => Expect::array(), 'references' => Expect::array(), 'tagged' => Expect::string(), 'inject' => Expect::bool(), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array(), 'reset' => Expect::array()]); |
| 152 |
} |
| 153 |
private static function getLocatorSchema() : Schema |
| 154 |
{ |
| 155 |
return Expect::structure(['implement' => Expect::string(), 'references' => Expect::array(), 'tagged' => Expect::string(), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array()]); |
| 156 |
} |
| 157 |
private static function getImportedSchema() : Schema |
| 158 |
{ |
| 159 |
return Expect::structure(['type' => Expect::string(), 'imported' => Expect::bool(), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array()]); |
| 160 |
} |
| 161 |
} |
| 162 |
|