Compiler::getParametersCode()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
3
namespace TheCodingMachine\Yaco;
4
5
use Interop\Container\Definition\DefinitionInterface;
6
use Interop\Container\Definition\DefinitionProviderInterface;
7
use TheCodingMachine\Yaco\Definition\DumpableInterface;
8
use TheCodingMachine\Yaco\Definition\InlineEntryInterface;
9
10
/**
11
 * A class that generates a PHP class (a container) from definitions.
12
 */
13
class Compiler
14
{
15
    /**
16
     * @var DefinitionInterface[]
17
     */
18
    private $definitions = [];
19
20
    /**
21
     * @var DumpableInterface[]
22
     */
23
    private $dumpableDefinitions = [];
24
25
    /**
26
     * The object in charge of converting container-interop definitions to our internal standard.
27
     *
28
     * @var DefinitionConverterInterface
29
     */
30
    private $converter;
31
32
    /**
33
     * @param DefinitionConverterInterface $converter The object in charge of converting container-interop definitions to our internal standard.
34
     */
35
    public function __construct(DefinitionConverterInterface $converter = null)
36
    {
37
        if ($converter === null) {
38
            $converter = new DefinitionConverter();
39
        }
40
        $this->converter = $converter;
41
    }
42
43
    /**
44
     * Adds a definition to the list of definitions managed by this compiler.
45
     *
46
     * @param string              $identifier
47
     * @param DefinitionInterface $definition
48
     */
49
    public function addDefinition($identifier, DefinitionInterface $definition)
50
    {
51
        $this->definitions[$identifier] = $definition;
52
        unset($this->dumpableDefinitions[$identifier]);
53
    }
54
55
    /**
56
     * Registers a new definition provider.
57
     *
58
     * @param DefinitionProviderInterface $definitionProvider
59
     */
60
    public function register(DefinitionProviderInterface $definitionProvider)
61
    {
62
        foreach ($definitionProvider->getDefinitions() as $identifier => $definition) {
63
            $this->addDefinition($identifier, $definition);
64
        }
65
    }
66
67
    /**
68
     * Adds a dumpable definition to the list of definitions managed by this compiler.
69
     * Note: a "dumpable" definition is a definition represented in Yaco internal format.
70
     *
71
     * @param DumpableInterface $dumpableDefinition
72
     */
73
    public function addDumpableDefinition(DumpableInterface $dumpableDefinition)
74
    {
75
        $this->dumpableDefinitions[$dumpableDefinition->getIdentifier()] = $dumpableDefinition;
76
        unset($this->definitions[$dumpableDefinition->getIdentifier()]);
77
    }
78
79
    /**
80
     * @param string $identifier
81
     *
82
     * @return bool
83
     */
84
    public function has($identifier)
85
    {
86
        return isset($this->dumpableDefinitions[$identifier]) || isset($this->definitions[$identifier]);
87
    }
88
89
    /**
90
     * Returns the dumpable definition matching the $identifier.
91
     *
92
     * @param string $identifier
93
     *
94
     * @return Definition\AliasDefinition|DumpableInterface|Definition\FactoryCallDefinition|Definition\ObjectDefinition|Definition\ParameterDefinition
95
     *
96
     * @throws CompilerException
97
     */
98
    public function getDumpableDefinition($identifier)
99
    {
100
        if (isset($this->dumpableDefinitions[$identifier])) {
101
            return $this->dumpableDefinitions[$identifier];
102
        } elseif (isset($this->definitions[$identifier])) {
103
            return $this->converter->convert($identifier, $this->definitions[$identifier]);
104
        } else {
105
            throw new CompilerException(sprintf('Unknown identifier in compiler: "%s"', $identifier));
106
        }
107
    }
108
109
    /**
110
     * @param string $className
111
     *
112
     * @return string
113
     */
114
    public function compile($className)
115
    {
116
        $classCode = <<<EOF
117
<?php
118
%s
119
120
use Mouf\Picotainer\Picotainer;
121
122
class %s extends Picotainer
123
{
124
    public function __construct(ContainerInterface \$delegateLookupContainer = null) {
125
        parent::__construct([
126
%s        ], \$delegateLookupContainer);
127
        \$this->objects = [
128
%s        ];
129
    }
130
}
131
132
EOF;
133
134
        list($shortClassName, $namespaceLine) = $this->splitFQCN($className);
135
136
        $closuresCode = '';
137
        $parametersCode = '';
138
139
        $convertedDefinitions = [];
140
        // Let's merge dumpable definitions with standard definitions.
141
        foreach ($this->definitions as $identifier => $definition) {
142
            $convertedDefinitions[$identifier] = $this->converter->convert($identifier, $definition);
143
        }
144
145
        $allDefinitions = $convertedDefinitions + $this->dumpableDefinitions;
146
147
        foreach ($allDefinitions as $identifier => $definition) {
148
            $inlineEntry = $definition->toPhpCode('$container', ['$container']);
149
150
            if ($inlineEntry->isLazilyEvaluated()) {
151
                $closuresCode .= '            '.var_export($identifier, true).' => '.$this->getClosureCode($inlineEntry).",\n";
152
            } else {
153
                $parametersCode .= '            '.var_export($identifier, true).' => '.$this->getParametersCode($inlineEntry).",\n";
154
            }
155
        }
156
157
        return sprintf($classCode, $namespaceLine, $shortClassName, $closuresCode, $parametersCode);
158
    }
159
160
    private function splitFQCN($className)
161
    {
162
        $pos = strrpos($className, '\\');
163
        if ($pos !== false) {
164
            $shortClassName = substr($className, $pos + 1);
165
            $namespaceLine = 'namespace '.substr($className, 0, $pos).';';
166
        } else {
167
            $shortClassName = $className;
168
            $namespaceLine = '';
169
        }
170
171
        return [
172
            $shortClassName,
173
            $namespaceLine,
174
        ];
175
    }
176
177
    private function getParametersCode(InlineEntryInterface $inlineEntry)
178
    {
179
        if (!empty($inlineEntry->getStatements())) {
180
            throw new CompilerException('An entry that contains parameters (not lazily loaded) cannot return statements.');
181
        }
182
183
        return $inlineEntry->getExpression();
184
    }
185
186
    private function getClosureCode(InlineEntryInterface $inlineEntry)
187
    {
188
        $code = $inlineEntry->getStatements();
189
        $code .= 'return '.$inlineEntry->getExpression().";\n";
190
191
        return sprintf("function(\$container) {\n%s}", $code);
192
    }
193
}
194