Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
annotationtrait.php
1<?php
2
4
24
32trait AnnotationTrait
33{
40 public static function annotateEntity(Entity $entity, $ufOnly = false, $separateTable = false)
41 {
42 $entityNamespace = trim($entity->getNamespace(), '\\');
43 $dataClass = $entity->getDataClass();
44
45 $objectClass = $entity->getObjectClass();
46 $objectClassName = $entity->getObjectClassName();
47 $objectDefaultClassName = Entity::getDefaultObjectClassName($entity->getName());
48
49 $collectionClass = $entity->getCollectionClass();
50 $collectionClassName = $entity->getCollectionClassName();
51 $collectionDefaultClassName = Entity::getDefaultCollectionClassName($entity->getName());
52
53 $code = [];
54 $objectCode = [];
55 $collectionCode = [];
56
57 $code[] = "namespace {$entityNamespace} {"; // start namespace
58 $code[] = "\t/**"; // start class annotations
59 $code[] = "\t * {$objectClassName}";
60 $code[] = "\t * @see {$dataClass}";
61 $code[] = "\t *";
62 $code[] = "\t * Custom methods:";
63 $code[] = "\t * ---------------";
64 $code[] = "\t *";
65
66 $exceptions = [];
67
68 foreach ($entity->getFields() as $field)
69 {
70 $objectFieldCode = [];
71 $collectionFieldCode = [];
72
73 if ($ufOnly && !($field instanceof UserTypeField))
74 {
75 continue;
76 }
77
78 try
79 {
80 if ($field instanceof ScalarField)
81 {
82 [$objectFieldCode, $collectionFieldCode] = static::annotateScalarField($field);
83 }
84 elseif ($field instanceof UserTypeField)
85 {
86 [$objectFieldCode, $collectionFieldCode] = static::annotateUserType($field);
87 }
88 elseif ($field instanceof ExpressionField)
89 {
90 [$objectFieldCode, $collectionFieldCode] = static::annotateExpression($field);
91 }
92 elseif ($field instanceof Reference)
93 {
94 [$objectFieldCode, $collectionFieldCode] = static::annotateReference($field);
95 }
96 elseif ($field instanceof OneToMany)
97 {
98 [$objectFieldCode, $collectionFieldCode] = static::annotateOneToMany($field);
99 }
100 elseif ($field instanceof ManyToMany)
101 {
102 [$objectFieldCode, $collectionFieldCode] = static::annotateManyToMany($field);
103 }
104
105 $objectCode = array_merge($objectCode, $objectFieldCode);
106 $collectionCode = array_merge($collectionCode, $collectionFieldCode);
107 }
108 catch (\Exception $e)
109 {
110 $exceptions[] = new \Exception(
111 "Can not annotate `{$entity->getFullName()}.{$field->getName()}` field", 0, $e
112 );
113 }
114 }
115
116 // return if there is no fields to annotate (e.g. empty uf)
117 if ($ufOnly && empty($objectCode))
118 {
119 return null;
120 }
121
122 $code = array_merge($code, $objectCode);
123
124 if (!$ufOnly)
125 {
126 // common class methods
127 $code[] = "\t *";
128 $code[] = "\t * Common methods:";
129 $code[] = "\t * ---------------";
130 $code[] = "\t *";
131 $code[] = "\t * @property-read \\".Entity::class." \$entity";
132 $code[] = "\t * @property-read array \$primary";
133 $code[] = "\t * @property-read int \$state @see \\".State::class;
134 $code[] = "\t * @property-read \\".Dictionary::class." \$customData";
135 $code[] = "\t * @property \\".Context::class." \$authContext";
136 $code[] = "\t * @method mixed get(\$fieldName)";
137 $code[] = "\t * @method mixed remindActual(\$fieldName)";
138 $code[] = "\t * @method mixed require(\$fieldName)";
139 $code[] = "\t * @method bool has(\$fieldName)";
140 $code[] = "\t * @method bool isFilled(\$fieldName)";
141 $code[] = "\t * @method bool isChanged(\$fieldName)";
142 $code[] = "\t * @method {$objectClass} set(\$fieldName, \$value)";
143 $code[] = "\t * @method {$objectClass} reset(\$fieldName)";
144 $code[] = "\t * @method {$objectClass} unset(\$fieldName)";
145 $code[] = "\t * @method void addTo(\$fieldName, \$value)";
146 $code[] = "\t * @method void removeFrom(\$fieldName, \$value)";
147 $code[] = "\t * @method void removeAll(\$fieldName)";
148 $code[] = "\t * @method \\".Result::class." delete()";
149 $code[] = "\t * @method void fill(\$fields = \\".FieldTypeMask::class."::ALL) flag or array of field names";
150 $code[] = "\t * @method mixed[] collectValues(\$valuesType = \Bitrix\Main\ORM\Objectify\Values::ALL, \$fieldsMask = \Bitrix\Main\ORM\Fields\FieldTypeMask::ALL)";
151 $code[] = "\t * @method \\".AddResult::class."|\\".UpdateResult::class."|\\".Result::class." save()";
152 $code[] = "\t * @method static {$objectClass} wakeUp(\$data)";
153 }
154
155 //$code[] = "\t *";
156 //$code[] = "\t * for parent class, @see \\".EntityObject::class;
157 // xTODO we can put path to the original file here
158 $code[] = "\t */"; // end class annotations
159 $code[] = "\tclass {$objectDefaultClassName} {";
160 $code[] = "\t\t/* @var {$dataClass} */";
161 $code[] = "\t\tstatic public \$dataClass = '{$dataClass}';";
162 $code[] = "\t\t/**";
163 $code[] = "\t\t * @param bool|array \$setDefaultValues";
164 $code[] = "\t\t */";
165 $code[] = "\t\tpublic function __construct(\$setDefaultValues = true) {}";
166 $code[] = "\t}"; // end class
167
168 // compatibility with default classes
169 if (strpos($objectClassName, Entity::DEFAULT_OBJECT_PREFIX) !== 0) // better to compare full classes definitions
170 {
171 $defaultObjectClassName = Entity::getDefaultObjectClassName($entity->getName());
172
173 // no need anymore as far as custom class inherits EO_
174 //$code[] = "\tclass_alias('{$objectClass}', '{$entityNamespace}\\{$defaultObjectClassName}');";
175 }
176
177 $code[] = "}"; // end namespace
178
179 // annotate collection class
180 $code[] = "namespace {$entityNamespace} {"; // start namespace
181 $code[] = "\t/**";
182 $code[] = "\t * {$collectionClassName}";
183 $code[] = "\t *";
184 $code[] = "\t * Custom methods:";
185 $code[] = "\t * ---------------";
186 $code[] = "\t *";
187
188 $code = array_merge($code, $collectionCode);
189
190 if (!$ufOnly)
191 {
192 $code[] = "\t *";
193 $code[] = "\t * Common methods:";
194 $code[] = "\t * ---------------";
195 $code[] = "\t *";
196 $code[] = "\t * @property-read \\".Entity::class." \$entity";
197 $code[] = "\t * @method void add({$objectClass} \$object)";
198 $code[] = "\t * @method bool has({$objectClass} \$object)";
199 $code[] = "\t * @method bool hasByPrimary(\$primary)";
200 $code[] = "\t * @method {$objectClass} getByPrimary(\$primary)";
201 $code[] = "\t * @method {$objectClass}[] getAll()";
202 $code[] = "\t * @method bool remove({$objectClass} \$object)";
203 $code[] = "\t * @method void removeByPrimary(\$primary)";
204 $code[] = "\t * @method void fill(\$fields = \\".FieldTypeMask::class."::ALL) flag or array of field names";
205 $code[] = "\t * @method static {$collectionClass} wakeUp(\$data)";
206 $code[] = "\t * @method \\".Result::class." save(\$ignoreEvents = false)";
207 $code[] = "\t * @method void offsetSet() ArrayAccess";
208 $code[] = "\t * @method void offsetExists() ArrayAccess";
209 $code[] = "\t * @method void offsetUnset() ArrayAccess";
210 $code[] = "\t * @method void offsetGet() ArrayAccess";
211 $code[] = "\t * @method void rewind() Iterator";
212 $code[] = "\t * @method {$objectClass} current() Iterator";
213 $code[] = "\t * @method mixed key() Iterator";
214 $code[] = "\t * @method void next() Iterator";
215 $code[] = "\t * @method bool valid() Iterator";
216 $code[] = "\t * @method int count() Countable";
217 $code[] = "\t * @method {$collectionClassName} merge(?{$collectionClassName} \$collection)";
218 $code[] = "\t * @method bool isEmpty()";
219 }
220
221 // xTODO we can put path to the original file here
222 $code[] = "\t */";
223 $code[] = "\tclass {$collectionDefaultClassName} implements \ArrayAccess, \Iterator, \Countable {";
224 $code[] = "\t\t/* @var {$dataClass} */";
225 $code[] = "\t\tstatic public \$dataClass = '{$dataClass}';";
226 $code[] = "\t}"; // end class
227
228 // compatibility with default classes
229 if (strpos($collectionClassName, Entity::DEFAULT_OBJECT_PREFIX) !== 0) // better to compare full classes definitions
230 {
231 $defaultCollectionClassName = Entity::getDefaultCollectionClassName($entity->getName());
232
233 // no need anymore as far as custom class inherits EO_
234 //$code[] = "\tclass_alias('{$entityNamespace}\\{$collectionClassName}', '{$entityNamespace}\\{$defaultCollectionClassName}');";
235 }
236
237 $code[] = "}"; // end namespace
238
239 if (!$ufOnly)
240 {
241 // annotate Table class
242 $dataClassName = $entity->getName().'Table';
243 $queryClassName = Entity::DEFAULT_OBJECT_PREFIX.$entity->getName().'_Query';
244 $resultClassName = Entity::DEFAULT_OBJECT_PREFIX.$entity->getName().'_Result';
245 $entityClassName = Entity::DEFAULT_OBJECT_PREFIX.$entity->getName().'_Entity';
246
247 $code[] = "namespace {$entityNamespace} {"; // start namespace
248
249 if (!$separateTable)
250 {
251 $code[] = "\t/**";
252 }
253
254 $codeTable = [];
255 $codeTable[] = " * @method static {$queryClassName} query()";
256 $codeTable[] = " * @method static {$resultClassName} getByPrimary(\$primary, array \$parameters = [])";
257 $codeTable[] = " * @method static {$resultClassName} getById(\$id)";
258 $codeTable[] = " * @method static {$resultClassName} getList(array \$parameters = [])";
259 $codeTable[] = " * @method static {$entityClassName} getEntity()";
260 $codeTable[] = " * @method static {$objectClass} createObject(\$setDefaultValues = true)";
261 $codeTable[] = " * @method static {$collectionClass} createCollection()";
262 $codeTable[] = " * @method static {$objectClass} wakeUpObject(\$row)";
263 $codeTable[] = " * @method static {$collectionClass} wakeUpCollection(\$rows)";
264
265 if (!$separateTable)
266 {
267 // add tabs
268 foreach ($codeTable as $i => $line)
269 {
270 $codeTable[$i] = "\t".$line;
271 }
272
273 $code = array_merge($code, $codeTable);
274 $code[] = "\t */";
275 $code[] = "\tclass {$dataClassName} extends \\".DataManager::class." {}";
276 }
277
278 // annotate Query class
279 $code[] = "\t/**";
280 $code[] = "\t * Common methods:";
281 $code[] = "\t * ---------------";
282 $code[] = "\t *";
283 $code[] = "\t * @method {$resultClassName} exec()";
284 $code[] = "\t * @method {$objectClass} fetchObject()";
285 $code[] = "\t * @method {$collectionClass} fetchCollection()";
286 $code[] = "\t *";
287 $code[] = "\t * Custom methods:";
288 $code[] = "\t * ---------------";
289 $code[] = "\t *";
290
291 foreach (get_class_methods($dataClass) as $method)
292 {
293 // search for with* methods
294 if (substr($method, 0, 4) === 'with')
295 {
296 $reflectionMethod = new \ReflectionMethod($dataClass, $method);
297
298 if ($reflectionMethod->isStatic())
299 {
300 $arguments = [];
301
302 // get parameters except the first one (query itself)
303 foreach (array_slice($reflectionMethod->getParameters(), 1) as $parameter)
304 {
305 $arguments[] = '$'.$parameter->getName();
306 }
307
308 $argumentsMeta = join(', ', $arguments);
309
310 $code[] = "\t * @see {$dataClass}::{$method}()";
311 $code[] = "\t * @method {$queryClassName} {$method}({$argumentsMeta})";
312 }
313 }
314 }
315
316 $code[] = "\t */";
317 $code[] = "\tclass {$queryClassName} extends \\".Query::class." {}";
318
319 // annotate Result class
320 $code[] = "\t/**";
321 $code[] = "\t * @method {$objectClass} fetchObject()";
322 $code[] = "\t * @method {$collectionClass} fetchCollection()";
323 $code[] = "\t */";
324 $code[] = "\tclass {$resultClassName} extends \\".\Bitrix\Main\ORM\Query\Result::class." {}";
325
326 // annotate Entity class
327 $code[] = "\t/**";
328 $code[] = "\t * @method {$objectClass} createObject(\$setDefaultValues = true)";
329 $code[] = "\t * @method {$collectionClass} createCollection()";
330 $code[] = "\t * @method {$objectClass} wakeUpObject(\$row)";
331 $code[] = "\t * @method {$collectionClass} wakeUpCollection(\$rows)";
332 $code[] = "\t */";
333 $code[] = "\tclass {$entityClassName} extends \\".Entity::class." {}";
334
335 $code[] = "}"; // end namespace
336 }
337
338 if (!$separateTable)
339 {
340 return join("\n", $code);
341 }
342 else
343 {
344 return [
345 join("\n", $codeTable),
346 join("\n", $code),
347 $exceptions
348 ];
349 }
350 }
351
352 public static function annotateScalarField(ScalarField $field)
353 {
354 // TODO no setter if it is reference-elemental (could expressions become elemental?)
355
356 $objectClass = $field->getEntity()->getObjectClass();
357 $getterDataType = $field->getGetterTypeHint();
358 $setterDataType = $field->getSetterTypeHint();
359 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
360
361 $objectCode = [];
362 $collectionCode = [];
363
364 $objectCode[] = "\t * @method {$getterDataType} get{$uName}()";
365 $objectCode[] = "\t * @method {$objectClass} set{$uName}({$setterDataType}|\\".SqlExpression::class." \${$lName})";
366
367 $objectCode[] = "\t * @method bool has{$uName}()";
368 $objectCode[] = "\t * @method bool is{$uName}Filled()";
369 $objectCode[] = "\t * @method bool is{$uName}Changed()";
370
371 $collectionCode[] = "\t * @method {$getterDataType}[] get{$uName}List()";
372
373 if (!$field->isPrimary())
374 {
375 $objectCode[] = "\t * @method {$getterDataType} remindActual{$uName}()";
376 $objectCode[] = "\t * @method {$getterDataType} require{$uName}()";
377
378 $objectCode[] = "\t * @method {$objectClass} reset{$uName}()";
379 $objectCode[] = "\t * @method {$objectClass} unset{$uName}()";
380
381 $objectCode[] = "\t * @method {$getterDataType} fill{$uName}()";
382 $collectionCode[] = "\t * @method {$getterDataType}[] fill{$uName}()";
383 }
384
385 return [$objectCode, $collectionCode];
386 }
387
388 public static function annotateUserType(UserTypeField $field)
389 {
390 // no setter
391 $objectClass = $field->getEntity()->getObjectClass();
392
394 $scalarFieldClass = $field->getValueType();
395 $dataType = (new $scalarFieldClass('TMP'))->getSetterTypeHint();
396 $dataType = $field->isMultiple() ? $dataType.'[]' : $dataType;
397 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
398
399 list($objectCode, $collectionCode) = static::annotateExpression($field);
400
401 // add setter
402 $objectCode[] = "\t * @method {$objectClass} set{$uName}({$dataType} \${$lName})";
403
404 $objectCode[] = "\t * @method bool is{$uName}Changed()";
405
406 return [$objectCode, $collectionCode];
407 }
408
409 public static function annotateExpression(ExpressionField $field)
410 {
411 // no setter
412 $objectClass = $field->getEntity()->getObjectClass();
413
414 $scalarFieldClass = $field->getValueType();
415 $dataType = (new $scalarFieldClass('TMP'))->getGetterTypeHint();
416 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
417
418 $objectCode = [];
419 $collectionCode = [];
420
421 $objectCode[] = "\t * @method {$dataType} get{$uName}()";
422 $objectCode[] = "\t * @method {$dataType} remindActual{$uName}()";
423 $objectCode[] = "\t * @method {$dataType} require{$uName}()";
424
425 $objectCode[] = "\t * @method bool has{$uName}()";
426 $objectCode[] = "\t * @method bool is{$uName}Filled()";
427
428 $collectionCode[] = "\t * @method {$dataType}[] get{$uName}List()";
429
430 $objectCode[] = "\t * @method {$objectClass} unset{$uName}()";
431
432 $objectCode[] = "\t * @method {$dataType} fill{$uName}()";
433 $collectionCode[] = "\t * @method {$dataType}[] fill{$uName}()";
434
435 return [$objectCode, $collectionCode];
436 }
437
438 public static function annotateReference(Reference $field)
439 {
440 if (!static::tryToFindEntity($field->getRefEntityName()))
441 {
442 return [[], []];
443 }
444
445 $objectClass = $field->getEntity()->getObjectClass();
446 $collectionClass = $field->getEntity()->getCollectionClass();
447 $collectionDataType = $field->getRefEntity()->getCollectionClass();
448
449 $getterTypeHint = $field->getGetterTypeHint();
450 $setterTypeHint = $field->getSetterTypeHint();
451
452 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
453
454 $objectCode = [];
455 $collectionCode = [];
456
457 $objectCode[] = "\t * @method {$getterTypeHint} get{$uName}()";
458 $objectCode[] = "\t * @method {$getterTypeHint} remindActual{$uName}()";
459 $objectCode[] = "\t * @method {$getterTypeHint} require{$uName}()";
460
461 $objectCode[] = "\t * @method {$objectClass} set{$uName}({$setterTypeHint} \$object)";
462 $objectCode[] = "\t * @method {$objectClass} reset{$uName}()";
463 $objectCode[] = "\t * @method {$objectClass} unset{$uName}()";
464
465 $objectCode[] = "\t * @method bool has{$uName}()";
466 $objectCode[] = "\t * @method bool is{$uName}Filled()";
467 $objectCode[] = "\t * @method bool is{$uName}Changed()";
468
469 $collectionCode[] = "\t * @method {$getterTypeHint}[] get{$uName}List()";
470 $collectionCode[] = "\t * @method {$collectionClass} get{$uName}Collection()";
471
472 $objectCode[] = "\t * @method {$getterTypeHint} fill{$uName}()";
473 $collectionCode[] = "\t * @method {$collectionDataType} fill{$uName}()";
474
475 return [$objectCode, $collectionCode];
476 }
477
478 public static function annotateOneToMany(OneToMany $field)
479 {
480 if (!static::tryToFindEntity($field->getRefEntityName()))
481 {
482 return [[], []];
483 }
484
485 $objectClass = $field->getEntity()->getObjectClass();
486 $collectionDataType = $field->getRefEntity()->getCollectionClass();
487 $objectVarName = lcfirst($field->getRefEntity()->getName());
488
489 $setterTypeHint = $field->getSetterTypeHint();
490
491 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
492
493 $objectCode = [];
494 $collectionCode = [];
495
496 $objectCode[] = "\t * @method {$collectionDataType} get{$uName}()";
497 $objectCode[] = "\t * @method {$collectionDataType} require{$uName}()";
498 $objectCode[] = "\t * @method {$collectionDataType} fill{$uName}()";
499
500 $objectCode[] = "\t * @method bool has{$uName}()";
501 $objectCode[] = "\t * @method bool is{$uName}Filled()";
502 $objectCode[] = "\t * @method bool is{$uName}Changed()";
503
504 $objectCode[] = "\t * @method void addTo{$uName}({$setterTypeHint} \${$objectVarName})";
505 $objectCode[] = "\t * @method void removeFrom{$uName}({$setterTypeHint} \${$objectVarName})";
506 $objectCode[] = "\t * @method void removeAll{$uName}()";
507
508 $objectCode[] = "\t * @method {$objectClass} reset{$uName}()";
509 $objectCode[] = "\t * @method {$objectClass} unset{$uName}()";
510
511 $collectionCode[] = "\t * @method {$collectionDataType}[] get{$uName}List()";
512 $collectionCode[] = "\t * @method {$collectionDataType} get{$uName}Collection()";
513 $collectionCode[] = "\t * @method {$collectionDataType} fill{$uName}()";
514
515 return [$objectCode, $collectionCode];
516 }
517
518 public static function annotateManyToMany(ManyToMany $field)
519 {
520 if (!static::tryToFindEntity($field->getRefEntityName()))
521 {
522 return [[], []];
523 }
524
525 $objectClass = $field->getEntity()->getObjectClass();
526 $collectionDataType = $field->getRefEntity()->getCollectionClass();
527 $objectVarName = lcfirst($field->getRefEntity()->getName());
528
529 $setterTypeHint = $field->getSetterTypeHint();
530
531 list($lName, $uName) = static::getFieldNameCamelCase($field->getName());
532
533 $objectCode = [];
534 $collectionCode = [];
535
536 $objectCode[] = "\t * @method {$collectionDataType} get{$uName}()";
537 $objectCode[] = "\t * @method {$collectionDataType} require{$uName}()";
538 $objectCode[] = "\t * @method {$collectionDataType} fill{$uName}()";
539
540 $objectCode[] = "\t * @method bool has{$uName}()";
541 $objectCode[] = "\t * @method bool is{$uName}Filled()";
542 $objectCode[] = "\t * @method bool is{$uName}Changed()";
543
544 $objectCode[] = "\t * @method void addTo{$uName}({$setterTypeHint} \${$objectVarName})";
545 $objectCode[] = "\t * @method void removeFrom{$uName}({$setterTypeHint} \${$objectVarName})";
546 $objectCode[] = "\t * @method void removeAll{$uName}()";
547
548 $objectCode[] = "\t * @method {$objectClass} reset{$uName}()";
549 $objectCode[] = "\t * @method {$objectClass} unset{$uName}()";
550
551 $collectionCode[] = "\t * @method {$collectionDataType}[] get{$uName}List()";
552 $collectionCode[] = "\t * @method {$collectionDataType} get{$uName}Collection()";
553 $collectionCode[] = "\t * @method {$collectionDataType} fill{$uName}()";
554
555 return [$objectCode, $collectionCode];
556 }
557
558 public static function tryToFindEntity($entityClass)
559 {
560 $entityClass = Entity::normalizeEntityClass($entityClass);
561
562 if (!class_exists($entityClass))
563 {
564 // try to find remote entity
565 $classParts = array_values(array_filter(
566 explode('\\', strtolower($entityClass))
567 ));
568
569 if ($classParts[0] == 'bitrix')
570 {
571 $moduleName = $classParts[1];
572 }
573 else
574 {
575 $moduleName = $classParts[0].'.'.$classParts[1];
576 }
577
578 if (!Loader::includeModule($moduleName) || !class_exists($entityClass))
579 {
580 return false;
581 }
582 }
583
584 if ((new \ReflectionClass($entityClass))->isAbstract())
585 {
586 return false;
587 }
588
589 return true;
590 }
591
592 protected static function getFieldNameCamelCase($fieldName)
593 {
594 $upperFirstName = StringHelper::snake2camel($fieldName);
595 $lowerFirstName = lcfirst($upperFirstName);
596
597 return [$lowerFirstName, $upperFirstName];
598 }
599}
static includeModule($moduleName)
Definition loader.php:69