Completed
Pull Request — 1.0 (#7)
by David
05:44
created

Compiler::getParametersCode()   A

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
        foreach ($definitionProvider->getDefinitions() as $identifier => $definition) {
62
            $this->addDefinition($identifier, $definition);
63
        }
64
    }
65
66
    /**
67
     * Adds a dumpable definition to the list of definitions managed by this compiler.
68
     * Note: a "dumpable" definition is a definition represented in Yaco internal format.
69
     *
70
     * @param DumpableInterface $dumpableDefinition
71
     */
72
    public function addDumpableDefinition(DumpableInterface $dumpableDefinition)
73
    {
74
        $this->dumpableDefinitions[$dumpableDefinition->getIdentifier()] = $dumpableDefinition;
75
        unset($this->definitions[$dumpableDefinition->getIdentifier()]);
76
    }
77
78
    /**
79
     * @param string $identifier
80
     * @return bool
81
     */
82
    public function has($identifier) {
83
        return isset($this->dumpableDefinitions[$identifier]) || isset($this->definitions[$identifier]);
84
    }
85
86
    /**
87
     * Returns the dumpable definition matching the $identifier
88
     * @param string $identifier
89
     * @return Definition\AliasDefinition|DumpableInterface|Definition\FactoryCallDefinition|Definition\ObjectDefinition|Definition\ParameterDefinition
90
     * @throws CompilerException
91
     */
92
    public function getDumpableDefinition($identifier) {
93
        if (isset($this->dumpableDefinitions[$identifier])) {
94
            return $this->dumpableDefinitions[$identifier];
95
        } elseif (isset($this->definitions[$identifier])) {
96
            return $this->converter->convert($identifier, $this->definitions[$identifier]);
97
        } else {
98
            throw new CompilerException(sprintf('Unknown identifier in compiler: "%s"', $identifier));
99
        }
100
    }
101
102
    /**
103
     * @param string $className
104
     *
105
     * @return string
106
     */
107
    public function compile($className)
108
    {
109
        $classCode = <<<EOF
110
<?php
111
%s
112
113
use Mouf\Picotainer\Picotainer;
114
115
class %s extends Picotainer
116
{
117
    public function __construct(ContainerInterface \$delegateLookupContainer = null) {
118
        parent::__construct([
119
%s        ], \$delegateLookupContainer);
120
        \$this->objects = [
121
%s        ];
122
    }
123
}
124
125
EOF;
126
127
        list($shortClassName, $namespaceLine) = $this->splitFQCN($className);
128
129
        $closuresCode = '';
130
        $parametersCode = '';
131
132
        $convertedDefinitions = [];
133
        // Let's merge dumpable definitions with standard definitions.
134
        foreach ($this->definitions as $identifier => $definition) {
135
            $convertedDefinitions[$identifier] = $this->converter->convert($identifier, $definition);
136
        }
137
138
        $allDefinitions = $convertedDefinitions + $this->dumpableDefinitions;
139
140
        foreach ($allDefinitions as $identifier => $definition) {
141
            $inlineEntry = $definition->toPhpCode('$container', ['$container']);
142
143
            if ($inlineEntry->isLazilyEvaluated()) {
144
                $closuresCode .= '            '.var_export($identifier, true).' => '.$this->getClosureCode($inlineEntry).",\n";
145
            } else {
146
                $parametersCode .= '            '.var_export($identifier, true).' => '.$this->getParametersCode($inlineEntry).",\n";
147
            }
148
        }
149
150
        return sprintf($classCode, $namespaceLine, $shortClassName, $closuresCode, $parametersCode);
151
    }
152
153
    private function splitFQCN($className)
154
    {
155
        $pos = strrpos($className, '\\');
156
        if ($pos !== false) {
157
            $shortClassName = substr($className, $pos + 1);
158
            $namespaceLine = 'namespace '.substr($className, 0, $pos).';';
159
        } else {
160
            $shortClassName = $className;
161
            $namespaceLine = '';
162
        }
163
164
        return [
165
            $shortClassName,
166
            $namespaceLine,
167
        ];
168
    }
169
170
    private function getParametersCode(InlineEntryInterface $inlineEntry)
171
    {
172
        if (!empty($inlineEntry->getStatements())) {
173
            throw new CompilerException('An entry that contains parameters (not lazily loaded) cannot return statements.');
174
        }
175
176
        return $inlineEntry->getExpression();
177
    }
178
179
    private function getClosureCode(InlineEntryInterface $inlineEntry)
180
    {
181
        $code = $inlineEntry->getStatements();
182
        $code .= 'return '.$inlineEntry->getExpression().";\n";
183
184
        return sprintf("function(\$container) {\n%s}", $code);
185
    }
186
}
187