1<?php
2
3declare(strict_types=1);
4
5namespace DI\Definition\Source;
6
7use DI\Definition\ArrayDefinition;
8use DI\Definition\AutowireDefinition;
9use DI\Definition\DecoratorDefinition;
10use DI\Definition\Definition;
11use DI\Definition\Exception\InvalidDefinition;
12use DI\Definition\FactoryDefinition;
13use DI\Definition\Helper\DefinitionHelper;
14use DI\Definition\ObjectDefinition;
15use DI\Definition\ValueDefinition;
16
17/**
18 * Turns raw definitions/definition helpers into definitions ready
19 * to be resolved or compiled.
20 *
21 * @author Matthieu Napoli <matthieu@mnapoli.fr>
22 */
23class DefinitionNormalizer
24{
25    /**
26     * @var Autowiring
27     */
28    private $autowiring;
29
30    public function __construct(Autowiring $autowiring)
31    {
32        $this->autowiring = $autowiring;
33    }
34
35    /**
36     * Normalize a definition that is *not* nested in another one.
37     *
38     * This is usually a definition declared at the root of a definition array.
39     *
40     * @param mixed $definition
41     * @param string $name The definition name.
42     * @param string[] $wildcardsReplacements Replacements for wildcard definitions.
43     *
44     * @throws InvalidDefinition
45     */
46    public function normalizeRootDefinition($definition, string $name, array $wildcardsReplacements = null) : Definition
47    {
48        if ($definition instanceof DefinitionHelper) {
49            $definition = $definition->getDefinition($name);
50        } elseif (is_array($definition)) {
51            $definition = new ArrayDefinition($definition);
52        } elseif ($definition instanceof \Closure) {
53            $definition = new FactoryDefinition($name, $definition);
54        } elseif (! $definition instanceof Definition) {
55            $definition = new ValueDefinition($definition);
56        }
57
58        // For a class definition, we replace * in the class name with the matches
59        // *Interface -> *Impl => FooInterface -> FooImpl
60        if ($wildcardsReplacements && $definition instanceof ObjectDefinition) {
61            $definition->replaceWildcards($wildcardsReplacements);
62        }
63
64        if ($definition instanceof AutowireDefinition) {
65            $definition = $this->autowiring->autowire($name, $definition);
66        }
67
68        $definition->setName($name);
69
70        try {
71            $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']);
72        } catch (InvalidDefinition $e) {
73            throw InvalidDefinition::create($definition, sprintf(
74                'Definition "%s" contains an error: %s',
75                $definition->getName(),
76                $e->getMessage()
77            ), $e);
78        }
79
80        return $definition;
81    }
82
83    /**
84     * Normalize a definition that is nested in another one.
85     *
86     * @param mixed $definition
87     * @return mixed
88     *
89     * @throws InvalidDefinition
90     */
91    public function normalizeNestedDefinition($definition)
92    {
93        $name = '<nested definition>';
94
95        if ($definition instanceof DefinitionHelper) {
96            $definition = $definition->getDefinition($name);
97        } elseif (is_array($definition)) {
98            $definition = new ArrayDefinition($definition);
99        } elseif ($definition instanceof \Closure) {
100            $definition = new FactoryDefinition($name, $definition);
101        }
102
103        if ($definition instanceof DecoratorDefinition) {
104            throw new InvalidDefinition('Decorators cannot be nested in another definition');
105        }
106
107        if ($definition instanceof AutowireDefinition) {
108            $definition = $this->autowiring->autowire($name, $definition);
109        }
110
111        if ($definition instanceof Definition) {
112            $definition->setName($name);
113
114            // Recursively traverse nested definitions
115            $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']);
116        }
117
118        return $definition;
119    }
120}
121