1
|
|
|
<?php |
2
|
|
|
//[PHPCOMPRESSOR(remove,start)] |
3
|
|
|
/** |
4
|
|
|
* Created by PhpStorm. |
5
|
|
|
* User: VITALYIEGOROV |
6
|
|
|
* Date: 09.12.15 |
7
|
|
|
* Time: 14:34 |
8
|
|
|
*/ |
9
|
|
|
namespace samsoncms\api; |
10
|
|
|
|
11
|
|
|
use samsoncms\api\generator\Metadata; |
12
|
|
|
use samsoncms\api\generator\Generator; |
13
|
|
|
use samsoncms\api\generator\exception\ParentEntityNotFound; |
14
|
|
|
use samsonframework\orm\DatabaseInterface; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Entity classes generator. |
18
|
|
|
* @package samsoncms\api |
19
|
|
|
*/ |
20
|
|
|
class GeneratorApi extends Generator |
21
|
|
|
{ |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Generator constructor. |
25
|
|
|
* @param DatabaseInterface $database Database instance |
26
|
|
|
* @throws ParentEntityNotFound |
27
|
|
|
* @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound |
28
|
|
|
*/ |
29
|
|
|
public function __construct(DatabaseInterface $database) |
30
|
|
|
{ |
31
|
|
|
parent::__construct($database); |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Fill metadata only with structures which have to be generated |
35
|
|
|
*/ |
36
|
|
|
$this->fillMetadata(); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Generate Query::where() analog for specific field. |
41
|
|
|
* |
42
|
|
|
* @param string $fieldName Field name |
43
|
|
|
* @param string $fieldId Field primary identifier |
44
|
|
|
* @param string $fieldType Field PHP type |
45
|
|
|
* @return string Generated PHP method code |
46
|
|
|
*/ |
47
|
|
|
protected function generateFieldConditionMethod($fieldName, $fieldId, $fieldType) |
48
|
|
|
{ |
49
|
|
|
$code = "\n\t" . '/**'; |
50
|
|
|
$code .= "\n\t" . ' * Add '.$fieldName.'(#' . $fieldId . ') field query condition.'; |
51
|
|
|
$code .= "\n\t" . ' * @param '.$fieldType.' $value Field value'; |
52
|
|
|
$code .= "\n\t" . ' * @return $this Chaining'; |
53
|
|
|
$code .= "\n\t" . ' * @see Generic::where()'; |
54
|
|
|
$code .= "\n\t" . ' */'; |
55
|
|
|
$code .= "\n\t" . 'public function ' . $fieldName . '($value, $relation = ArgumentInterface::EQUAL)'; |
56
|
|
|
$code .= "\n\t" . "{"; |
57
|
|
|
$code .= "\n\t\t" . 'return $this->where("'.$fieldName.'", $value, $relation);'; |
58
|
|
|
|
59
|
|
|
return $code . "\n\t" . "}"."\n"; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Generate Query::where() analog for specific field. |
64
|
|
|
* |
65
|
|
|
* @param string $fieldName Field name |
66
|
|
|
* @param string $fieldId Field primary identifier |
67
|
|
|
* @param string $fieldType Field PHP type |
68
|
|
|
* @return string Generated PHP method code |
69
|
|
|
*/ |
70
|
|
|
protected function generateLocalizedFieldConditionMethod($fieldName, $fieldId, $fieldType) |
71
|
|
|
{ |
72
|
|
|
$code = "\n\t" . '/**'; |
73
|
|
|
$code .= "\n\t" . ' * Add '.$fieldName.'(#' . $fieldId . ') field query condition.'; |
74
|
|
|
$code .= "\n\t" . ' * @param '.Field::phpType($fieldType).' $value Field value'; |
75
|
|
|
$code .= "\n\t" . ' * @return $this Chaining'; |
76
|
|
|
$code .= "\n\t" . ' * @see Generic::where()'; |
77
|
|
|
$code .= "\n\t" . ' */'; |
78
|
|
|
$code .= "\n\t" . 'public function ' . $fieldName . '($value)'; |
79
|
|
|
$code .= "\n\t" . "{"; |
80
|
|
|
$code .= "\n\t\t" . 'return $this->where("'.$fieldName.'", $value);'; |
81
|
|
|
|
82
|
|
|
return $code . "\n\t" . "}"."\n"; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Generate FieldsTable::values() analog for specific field. |
87
|
|
|
* |
88
|
|
|
* @param string $fieldName Field name |
89
|
|
|
* @param string $fieldId Field primary identifier |
90
|
|
|
* @param string $fieldType Field PHP type |
91
|
|
|
* @return string Generated PHP method code |
92
|
|
|
*/ |
93
|
|
|
protected function generateTableFieldMethod($fieldName, $fieldId, $fieldType) |
94
|
|
|
{ |
95
|
|
|
$code = "\n\t" . '/**'; |
96
|
|
|
$code .= "\n\t" . ' * Get table column '.$fieldName.'(#' . $fieldId . ') values.'; |
97
|
|
|
$code .= "\n\t" . ' * @return array Collection('.Field::phpType($fieldType).') of table column values'; |
98
|
|
|
$code .= "\n\t" . ' */'; |
99
|
|
|
$code .= "\n\t" . 'public function ' . $fieldName . '()'; |
100
|
|
|
$code .= "\n\t" . "{"; |
101
|
|
|
$code .= "\n\t\t" . 'return $this->values('.$fieldId.');'; |
102
|
|
|
|
103
|
|
|
return $code . "\n\t" . "}"."\n"; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Generate constructor for table class. |
108
|
|
|
*/ |
109
|
|
|
protected function generateConstructorTableClass() |
110
|
|
|
{ |
111
|
|
|
$class = "\n\t".'/**'; |
112
|
|
|
$class .= "\n\t".' * @param QueryInterface $query Database query instance'; |
113
|
|
|
$class .= "\n\t".' * @param ViewInterface $renderer Rendering instance'; |
114
|
|
|
$class .= "\n\t".' * @param integer $entityID Entity identifier to whom this table belongs'; |
115
|
|
|
$class .= "\n\t".' * @param string $locale Localization identifier'; |
116
|
|
|
$class .= "\n\t".' */'; |
117
|
|
|
$class .= "\n\t".'public function __construct(QueryInterface $query, ViewInterface $renderer, $entityID, $locale = null)'; |
118
|
|
|
$class .= "\n\t".'{'; |
119
|
|
|
$class .= "\n\t\t".'parent::__construct($query, $renderer, static::$navigationIDs, $entityID, $locale);'; |
120
|
|
|
$class .= "\n\t".'}'."\n"; |
121
|
|
|
|
122
|
|
|
return $class; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Generate constructor for application class. |
127
|
|
|
*/ |
128
|
|
View Code Duplication |
protected function generateConstructorApplicationClass() |
|
|
|
|
129
|
|
|
{ |
130
|
|
|
$class = "\n\t".'/**'; |
131
|
|
|
$class .= "\n\t".' * Render materials list with pager'; |
132
|
|
|
$class .= "\n\t".' *'; |
133
|
|
|
$class .= "\n\t".' * @param string $navigationId Structure identifier'; |
134
|
|
|
$class .= "\n\t".' * @param string $search Keywords to filter table'; |
135
|
|
|
$class .= "\n\t".' * @param int $page Current table page'; |
136
|
|
|
$class .= "\n\t".' * @return array Asynchronous response containing status and materials list with pager on success'; |
137
|
|
|
$class .= "\n\t".' * or just status on asynchronous controller failure'; |
138
|
|
|
$class .= "\n\t".' */'; |
139
|
|
|
$class .= "\n\t".'public function __async_collection($navigationId = \'0\', $search = \'\', $page = 1)'; |
140
|
|
|
$class .= "\n\t".'{'; |
141
|
|
|
$class .= "\n\t\t".'return parent::__async_collection(self::$navigation, $search, $page);'; |
142
|
|
|
$class .= "\n\t".'}'."\n"; |
143
|
|
|
|
144
|
|
|
return $class; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Generate constructor for application class. |
149
|
|
|
*/ |
150
|
|
View Code Duplication |
protected function generateConstructorApplicationCollectionClass() |
|
|
|
|
151
|
|
|
{ |
152
|
|
|
$class = "\n\t".'/**'; |
153
|
|
|
$class .= "\n\t".' * Generic collection constructor'; |
154
|
|
|
$class .= "\n\t".' *'; |
155
|
|
|
$class .= "\n\t".' * @param RenderInterface $renderer View render object'; |
156
|
|
|
$class .= "\n\t".' * @param QueryInterface $query Query object'; |
157
|
|
|
$class .= "\n\t".' */'; |
158
|
|
|
$class .= "\n\t".'public function __async_collection($renderer, $query = null, $pager = null)'; |
159
|
|
|
$class .= "\n\t".'{'; |
160
|
|
|
$class .= "\n\t\t".'return parent::__async_collection($renderer, $query = null, $pager = null);'; |
161
|
|
|
$class .= "\n\t\t".'$this->fields = array('; |
162
|
|
|
$class .= "\n\t\t\t".'new Control(),'; |
163
|
|
|
$class .= "\n\t\t".');'; |
164
|
|
|
$class .= "\n\t".'}'."\n"; |
165
|
|
|
|
166
|
|
|
return $class; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Create fields table row PHP class code. |
171
|
|
|
* |
172
|
|
|
* @param Metadata $metadata metadata of entity |
173
|
|
|
* @param string $namespace Namespace of generated class |
174
|
|
|
* |
175
|
|
|
* @return string Generated entity query PHP class code |
176
|
|
|
* @throws exception\AdditionalFieldTypeNotFound |
177
|
|
|
*/ |
178
|
|
|
protected function createTableRowClass(Metadata $metadata, $namespace = __NAMESPACE__) |
|
|
|
|
179
|
|
|
{ |
180
|
|
|
$this->generator |
181
|
|
|
->multiComment(array('Class for getting "' . $metadata->entityRealName . '" fields table rows')) |
182
|
|
|
->defClass($this->entityName($metadata->entityRealName) . 'TableRow', 'Row'); |
183
|
|
|
|
184
|
|
|
$fieldIDs = array(); |
185
|
|
|
foreach ($this->navigationFields($metadata->entityID) as $fieldID => $fieldRow) { |
186
|
|
|
$fieldName = $this->fieldName($fieldRow['Name']); |
187
|
|
|
|
188
|
|
|
// Fill field ids array |
189
|
|
|
$fieldIDs[$fieldName] = $fieldID; |
190
|
|
|
|
191
|
|
|
$this->generator |
192
|
|
|
->commentVar($metadata->allFieldTypes[$fieldID], $fieldRow['Description'] . ' Field #' . $fieldID . ' variable name') |
193
|
|
|
->defClassConst('F_' . strtoupper($fieldName), $fieldName) |
194
|
|
|
->commentVar($metadata->allFieldTypes[$fieldID], $fieldRow['Description'] . ' Field #' . $fieldID . ' row value') |
195
|
|
|
->defVar('public $' . $fieldName) |
196
|
|
|
->text("\n"); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
return $this->generator |
200
|
|
|
->commentVar('array', 'Collection of additional fields identifiers') |
201
|
|
|
->defClassVar('$fieldIDs', 'public static', $fieldIDs) |
|
|
|
|
202
|
|
|
->endClass() |
203
|
|
|
->flush(); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Create fields table PHP class code. |
208
|
|
|
* |
209
|
|
|
* @param Metadata $metadata metadata of entity |
210
|
|
|
* @param string $namespace Namespace of generated class |
211
|
|
|
* |
212
|
|
|
* @return string Generated entity query PHP class code |
213
|
|
|
* @throws exception\AdditionalFieldTypeNotFound |
214
|
|
|
*/ |
215
|
|
|
protected function createTableClass(Metadata $metadata, $namespace = __NAMESPACE__) |
|
|
|
|
216
|
|
|
{ |
217
|
|
|
$this->generator |
218
|
|
|
->multiComment(array('Class for getting "'.$metadata->entityRealName.'" fields table')) |
219
|
|
|
->defClass($this->entityName($metadata->entityRealName) . 'Table', 'FieldsTable'); |
220
|
|
|
|
221
|
|
|
// Iterate additional fields |
222
|
|
|
$fields = array(); |
223
|
|
|
foreach ($this->navigationFields($metadata->entityID) as $fieldID => $fieldRow) { |
224
|
|
|
$fieldName = $this->fieldName($fieldRow['Name']); |
225
|
|
|
|
226
|
|
|
$this->generator |
227
|
|
|
->text($this->generateTableFieldMethod( |
228
|
|
|
$fieldName, |
229
|
|
|
$fieldRow[Field::F_PRIMARY], |
230
|
|
|
$fieldRow[Field::F_TYPE] |
231
|
|
|
)) |
232
|
|
|
->commentVar($metadata->allFieldTypes[$fieldID], $fieldRow['Description'] . ' Field #' . $fieldID . ' variable name') |
233
|
|
|
->defClassConst('F_' . $fieldName, $fieldName); |
234
|
|
|
|
235
|
|
|
// Collection original to new one field names |
236
|
|
|
$fields[$fieldRow['Name']] = $fieldName; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
// TODO: Add generator method generation logic |
240
|
|
|
$constructor = $this->generateConstructorTableClass(); |
241
|
|
|
|
242
|
|
|
$this->generator->text($constructor); |
243
|
|
|
|
244
|
|
|
return $this->generator |
245
|
|
|
->commentVar('array', 'Collection of real additional field names') |
246
|
|
|
->defClassVar('$fieldsRealNames', 'public static', $fields) |
|
|
|
|
247
|
|
|
->commentVar('array', 'Collection of navigation identifiers') |
248
|
|
|
->defClassVar('$navigationIDs', 'protected static', array($metadata->entityID)) |
|
|
|
|
249
|
|
|
->commentVar('string', 'Row class name') |
250
|
|
|
->defClassVar('$identifier', 'protected', $this->fullEntityName($this->entityName($metadata->entityRealName) . 'TableRow')) |
251
|
|
|
->endClass() |
252
|
|
|
->flush(); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Create entity PHP class code. |
257
|
|
|
* |
258
|
|
|
* @param Metadata $metadata Entity metadata |
259
|
|
|
* @param string $namespace Namespace of generated class |
260
|
|
|
* @return string Generated entity query PHP class code |
261
|
|
|
*/ |
262
|
|
|
protected function createEntityClass(Metadata $metadata, $namespace = __NAMESPACE__) |
263
|
|
|
{ |
264
|
|
|
/** |
265
|
|
|
* TODO: Parent problem |
266
|
|
|
* Should be changed to merging fields instead of extending with OOP for structure_relation support |
267
|
|
|
* or creating traits and using them on shared parent entities. |
268
|
|
|
*/ |
269
|
|
|
|
270
|
|
|
$this->generator |
271
|
|
|
->multiComment(array('"'.$metadata->entityRealName.'" entity class')) |
272
|
|
|
->defClass($metadata->entity, null !== $metadata->parent ? $this->fullEntityName($metadata->parent->entity, $namespace) : 'Entity') |
273
|
|
|
->commentVar('string', '@deprecated Entity full class name, use ::class') |
274
|
|
|
->defClassConst('ENTITY', $this->fullEntityName($metadata->entity, $namespace)) |
275
|
|
|
->commentVar('string', 'Entity manager full class name') |
276
|
|
|
->defClassConst('MANAGER', $this->fullEntityName($metadata->entity, $namespace).'Query') |
277
|
|
|
->commentVar('string', 'Entity database identifier') |
278
|
|
|
->defClassConst('IDENTIFIER', $metadata->entityID) |
279
|
|
|
->commentVar('string', 'Not transliterated entity name') |
280
|
|
|
->defClassVar('$viewName', 'protected static', $metadata->entityRealName); |
281
|
|
|
|
282
|
|
|
foreach ($metadata->allFieldIDs as $fieldID => $fieldName) { |
283
|
|
|
$this->generator |
284
|
|
|
->commentVar('string', $metadata->fieldDescriptions[$fieldID].' variable name') |
285
|
|
|
->defClassConst('F_' . $fieldName, $fieldName) |
286
|
|
|
->commentVar($metadata->allFieldTypes[$fieldID], $metadata->fieldDescriptions[$fieldID]) |
287
|
|
|
->defClassVar('$' . $fieldName, 'public'); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
return $this->generator |
291
|
|
|
->defClassVar('$_sql_select', 'public static ', $metadata->arSelect) |
|
|
|
|
292
|
|
|
->defClassVar('$_attributes', 'public static ', $metadata->arAttributes) |
|
|
|
|
293
|
|
|
->defClassVar('$_map', 'public static ', $metadata->arMap) |
|
|
|
|
294
|
|
|
->defClassVar('$_sql_from', 'public static ', $metadata->arFrom) |
|
|
|
|
295
|
|
|
->defClassVar('$_own_group', 'public static ', $metadata->arGroup) |
|
|
|
|
296
|
|
|
->defClassVar('$_relation_alias', 'public static ', $metadata->arRelationAlias) |
|
|
|
|
297
|
|
|
->defClassVar('$_relation_type', 'public static ', $metadata->arRelationType) |
|
|
|
|
298
|
|
|
->defClassVar('$_relations', 'public static ', $metadata->arRelations) |
|
|
|
|
299
|
|
|
->defClassVar('$fieldIDs', 'protected static ', $metadata->allFieldIDs) |
|
|
|
|
300
|
|
|
->defClassVar('$fieldValueColumns', 'protected static ', $metadata->allFieldValueColumns) |
|
|
|
|
301
|
|
|
->endClass() |
302
|
|
|
->flush(); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Create entity query PHP class code. |
307
|
|
|
* |
308
|
|
|
* @param Metadata $metadata Entity metadata |
309
|
|
|
* @param string $suffix Generated class name suffix |
310
|
|
|
* @param string $defaultParent Parent class name |
311
|
|
|
* @param string $namespace Namespace of generated class |
312
|
|
|
* |
313
|
|
|
* @return string Generated entity query PHP class code |
314
|
|
|
*/ |
315
|
|
|
protected function createQueryClass(Metadata $metadata, $suffix = 'Query', $defaultParent = '\samsoncms\api\query\Entity', $namespace = __NAMESPACE__) |
316
|
|
|
{ |
317
|
|
|
//$navigationID, $navigationName, $entityName, $navigationFields, $parentClass = '\samsoncms\api\query\Entity' |
318
|
|
|
$this->generator |
319
|
|
|
->multiComment(array('Class for fetching "'.$metadata->entityRealName.'" instances from database')) |
320
|
|
|
->defClass($metadata->entity.$suffix, $defaultParent) |
321
|
|
|
; |
322
|
|
|
|
323
|
|
|
foreach ($metadata->allFieldIDs as $fieldID => $fieldName) { |
324
|
|
|
// TODO: Add different method generation depending on their field type |
325
|
|
|
$this->generator->text($this->generateFieldConditionMethod( |
326
|
|
|
$fieldName, |
327
|
|
|
$fieldID, |
328
|
|
|
$metadata->allFieldTypes[$fieldID] |
329
|
|
|
)); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return $this->generator |
333
|
|
|
->commentVar('array', 'Collection of real additional field names') |
334
|
|
|
->defClassVar('$fieldRealNames', 'public static', $metadata->realNames) |
|
|
|
|
335
|
|
|
->commentVar('array', 'Collection of additional field names') |
336
|
|
|
->defClassVar('$fieldNames', 'public static', $metadata->allFieldNames) |
|
|
|
|
337
|
|
|
// TODO: two above fields should be protected |
338
|
|
|
->commentVar('array', 'Collection of navigation identifiers') |
339
|
|
|
->defClassVar('$navigationIDs', 'protected static', array($metadata->entityID)) |
|
|
|
|
340
|
|
|
->commentVar('string', 'Entity full class name') |
341
|
|
|
->defClassVar('$identifier', 'protected static', $this->fullEntityName($metadata->entity, $namespace)) |
342
|
|
|
->commentVar('array', 'Collection of localized additional fields identifiers') |
343
|
|
|
->defClassVar('$localizedFieldIDs', 'protected static', $metadata->localizedFieldIDs) |
|
|
|
|
344
|
|
|
->commentVar('array', 'Collection of NOT localized additional fields identifiers') |
345
|
|
|
->defClassVar('$notLocalizedFieldIDs', 'protected static', $metadata->notLocalizedFieldIDs) |
|
|
|
|
346
|
|
|
->commentVar('array', 'Collection of localized additional fields identifiers') |
347
|
|
|
->defClassVar('$fieldIDs', 'protected static', $metadata->allFieldIDs) |
|
|
|
|
348
|
|
|
->commentVar('array', 'Collection of additional fields value column names') |
349
|
|
|
->defClassVar('$fieldValueColumns', 'protected static', $metadata->allFieldValueColumns) |
|
|
|
|
350
|
|
|
->endClass() |
351
|
|
|
->flush() |
352
|
|
|
; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* Generate entity classes. |
357
|
|
|
* |
358
|
|
|
* @param string $namespace Base namespace for generated classes |
359
|
|
|
* @return string Generated PHP code for entity classes |
360
|
|
|
* @throws ParentEntityNotFound |
361
|
|
|
* @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound |
362
|
|
|
*/ |
363
|
|
|
public function createEntityClasses($namespace = __NAMESPACE__) |
364
|
|
|
{ |
365
|
|
|
|
366
|
|
|
$classes = "\n" . 'namespace ' . $namespace . ';'; |
367
|
|
|
$classes .= "\n"; |
368
|
|
|
$classes .= "\n" . 'use '.$namespace.'\renderable\FieldsTable;'; |
369
|
|
|
$classes .= "\n" . 'use '.$namespace.'\field\Row;'; |
370
|
|
|
$classes .= "\n" . 'use \samsonframework\core\ViewInterface;'; |
371
|
|
|
$classes .= "\n" . 'use \samsonframework\orm\ArgumentInterface;'; |
372
|
|
|
$classes .= "\n" . 'use \samsonframework\orm\QueryInterface;'; |
373
|
|
|
$classes .= "\n" . 'use \samson\activerecord\dbQuery;'; |
374
|
|
|
$classes .= "\n"; |
375
|
|
|
|
376
|
|
|
// Iterate all entities metadata |
377
|
|
|
foreach ($this->metadata as $metadata) { |
378
|
|
|
|
379
|
|
|
// Generate classes of default type |
380
|
|
|
if ($metadata->type === Metadata::TYPE_DEFAULT) { |
381
|
|
|
|
382
|
|
|
$classes .= $this->createEntityClass($metadata); |
383
|
|
|
$classes .= $this->createQueryClass($metadata); |
384
|
|
|
$classes .= $this->createQueryClass($metadata, 'Collection', '\samsoncms\api\renderable\Collection'); |
385
|
|
|
|
386
|
|
|
// Generate classes of table type |
387
|
|
|
} else if ($metadata->type === Metadata::TYPE_TABLE) { |
388
|
|
|
|
389
|
|
|
$classes .= $this->createTableRowClass($metadata); |
390
|
|
|
$classes .= $this->createTableClass($metadata); |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
// Make correct code formatting |
395
|
|
|
return $this->formatTab($classes); |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
//[PHPCOMPRESSOR(remove,end)] |
400
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.