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