1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
/** |
3
|
|
|
* Created by Vitaly Iegorov <[email protected]>. |
4
|
|
|
* on 03.09.16 at 11:37 |
5
|
|
|
*/ |
6
|
|
|
namespace samsonframework\generator; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Abstract code generator. |
10
|
|
|
* |
11
|
|
|
* @author Vitaly Egorov <[email protected]> |
12
|
|
|
*/ |
13
|
|
|
abstract class AbstractGenerator |
14
|
|
|
{ |
15
|
|
|
/** @var AbstractGenerator Parent class generator */ |
16
|
|
|
protected $parent; |
17
|
|
|
|
18
|
|
|
/** @var array Generated code grouped by generator class name */ |
19
|
|
|
protected $generatedCode = []; |
20
|
|
|
|
21
|
|
|
/** @var int Indentation level */ |
22
|
|
|
protected $indentation = 0; |
23
|
|
|
|
24
|
|
|
/** @var bool Flag that class has already had generated its code */ |
25
|
|
|
protected $isGenerated = false; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* AbstractGenerator constructor. |
29
|
|
|
* |
30
|
|
|
* @param AbstractGenerator $parent Parent generator |
31
|
|
|
*/ |
32
|
50 |
|
public function __construct(AbstractGenerator $parent = null) |
33
|
|
|
{ |
34
|
50 |
|
$this->parent = $parent; |
35
|
50 |
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Close current generator and return parent. |
39
|
|
|
* |
40
|
|
|
* @return AbstractGenerator|ClassGenerator|FunctionGenerator|MethodGenerator|PropertyGenerator|ClassConstantGenerator|ConditionGenerator|IfGenerator |
41
|
|
|
*/ |
42
|
9 |
|
public function end() : AbstractGenerator |
43
|
|
|
{ |
44
|
|
|
// Generate code |
45
|
9 |
|
$generatedCode = $this->code(); |
46
|
|
|
|
47
|
|
|
// Avoid creating empty strings |
48
|
9 |
|
if (!$this->isGenerated && $generatedCode !== '') { |
49
|
|
|
// Create array item |
50
|
9 |
|
$class = get_class($this); |
51
|
9 |
|
if (!array_key_exists($class, $this->parent->generatedCode)) { |
52
|
9 |
|
$this->parent->generatedCode[$class] = []; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
// Pass generated code to parent |
56
|
9 |
|
$this->parent->generatedCode[$class][] = $generatedCode; |
57
|
|
|
|
58
|
|
|
// Set flag that we already generated code for this class |
59
|
9 |
|
$this->isGenerated = true; |
60
|
|
|
} |
61
|
|
|
|
62
|
9 |
|
return $this->parent; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Generate code. |
67
|
|
|
* |
68
|
|
|
* @return string Generated code |
69
|
|
|
*/ |
70
|
|
|
abstract public function code(): string; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Set Comments block. |
74
|
|
|
* |
75
|
|
|
* @return CommentsGenerator Comments block generator |
76
|
|
|
*/ |
77
|
1 |
|
public function defComment() : CommentsGenerator |
78
|
|
|
{ |
79
|
1 |
|
return (new CommentsGenerator($this))->setIndentation($this->indentation); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Decrease indentation. |
84
|
|
|
* |
85
|
|
|
* @param int $indentation |
86
|
|
|
* |
87
|
|
|
* @return $this|AbstractGenerator|ClassGenerator |
88
|
|
|
*/ |
89
|
21 |
|
public function setIndentation(int $indentation): AbstractGenerator |
90
|
|
|
{ |
91
|
21 |
|
$this->indentation = $indentation; |
92
|
|
|
|
93
|
21 |
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Build nested class code array. |
98
|
|
|
* |
99
|
|
|
* @param string $className Nested class name |
100
|
|
|
* @param array $formattedCode Collection of code |
101
|
|
|
* |
102
|
|
|
* @return array Collection of code with added nested class code |
103
|
|
|
*/ |
104
|
20 |
|
protected function buildNestedCode(string $className, array $formattedCode = []): array |
105
|
|
|
{ |
106
|
20 |
|
$code = $this->getNestedCode($className); |
107
|
20 |
|
if ($code !== '') { |
108
|
7 |
|
$formattedCode[] = $code; |
109
|
|
|
} |
110
|
|
|
|
111
|
20 |
|
return $formattedCode; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Get generated nested code. |
116
|
|
|
* |
117
|
|
|
* @param string $className Nested class name |
118
|
|
|
* |
119
|
|
|
* @return string Generated nested code or empty string |
120
|
|
|
*/ |
121
|
29 |
|
protected function getNestedCode(string $className): string |
122
|
|
|
{ |
123
|
29 |
|
if (array_key_exists($className, $this->generatedCode)) { |
124
|
9 |
|
return ltrim(implode("", $this->generatedCode[$className]), "\n"); |
125
|
|
|
} else { |
126
|
27 |
|
return ''; |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Generate correct value. |
132
|
|
|
* |
133
|
|
|
* Method handles arrays, numerics, strings and constants. |
134
|
|
|
* |
135
|
|
|
* @param mixed $value Value |
136
|
|
|
* |
137
|
|
|
* @return mixed Value |
138
|
|
|
*/ |
139
|
4 |
|
protected function parseValue($value) |
140
|
|
|
{ |
141
|
|
|
// If item value is array - recursion |
142
|
4 |
|
if (is_array($value)) { |
143
|
|
|
return $this->arrayValue($value); |
144
|
4 |
|
} elseif (is_numeric($value) || is_float($value)) { |
145
|
|
|
return $value; |
146
|
4 |
|
} elseif ($value === null) { |
147
|
4 |
|
return null; |
148
|
|
|
} else { |
149
|
|
|
try { // Try to evaluate |
150
|
|
|
eval('$value2 = ' . $value . ';'); |
151
|
|
|
return $value; |
152
|
|
|
} catch (\Throwable $e) { // Consider it as a string |
|
|
|
|
153
|
|
|
return '\''.$value.'\''; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Get array values definition. |
160
|
|
|
* |
161
|
|
|
* @param array $items Array key-value pairs collection |
162
|
|
|
* |
163
|
|
|
* @return string Array value definition |
164
|
|
|
*/ |
165
|
|
|
protected function arrayValue(array $items = array()) |
166
|
|
|
{ |
167
|
|
|
$result = ['[']; |
168
|
|
|
if (count($items)) { |
169
|
|
|
$this->increaseIndentation(); |
170
|
|
|
|
171
|
|
|
// Iterate array items |
172
|
|
|
foreach ($items as $key => $value) { |
173
|
|
|
// Start array key definition |
174
|
|
|
$result[] = "\n" |
175
|
|
|
. $this->indentation($this->indentation) |
176
|
|
|
. $this->parseValue($key) |
177
|
|
|
. ' => ' |
178
|
|
|
. $this->parseValue($value) |
179
|
|
|
. ','; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$this->decreaseIndentation(); |
183
|
|
|
} |
184
|
|
|
$result[] = "\n".$this->indentation($this->indentation).']'; |
185
|
|
|
|
186
|
|
|
return implode('', $result); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Increase indentation. |
191
|
|
|
* |
192
|
|
|
* @return $this|AbstractGenerator |
193
|
|
|
*/ |
194
|
6 |
|
public function increaseIndentation(): AbstractGenerator |
195
|
|
|
{ |
196
|
6 |
|
return $this->setIndentation($this->indentation + 1); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Get indentation string. |
201
|
|
|
* |
202
|
|
|
* @param int $indentation Code level |
203
|
|
|
* |
204
|
|
|
* @return string Indentation string |
205
|
|
|
*/ |
206
|
41 |
|
protected function indentation(int $indentation = 0): string |
207
|
|
|
{ |
208
|
41 |
|
return implode('', $indentation > 0 ? array_fill(0, $indentation, ' ') : []); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Decrease indentation. |
213
|
|
|
* |
214
|
|
|
* @return $this|AbstractGenerator |
215
|
|
|
*/ |
216
|
|
|
public function decreaseIndentation(): AbstractGenerator |
217
|
|
|
{ |
218
|
|
|
return $this->setIndentation($this->indentation - 1); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.