Failed Conditions
Push — master ( 9005c5...76e215 )
by Marco
64:47
created

EntityGenerator::generateEmbeddableConstructor()   C

Complexity

Conditions 10
Paths 120

Size

Total Lines 86
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 47
CRAP Score 10.0008

Importance

Changes 0
Metric Value
dl 0
loc 86
ccs 47
cts 48
cp 0.9792
rs 5.0331
c 0
b 0
f 0
cc 10
eloc 52
nc 120
nop 1
crap 10.0008

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Tools;
21
22
use Doctrine\Common\Collections\Collection;
23
use Doctrine\Common\Util\Inflector;
24
use Doctrine\DBAL\Types\Type;
25
use Doctrine\ORM\Mapping\ClassMetadataInfo;
26
27
/**
28
 * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances.
29
 *
30
 *     [php]
31
 *     $classes = $em->getClassMetadataFactory()->getAllMetadata();
32
 *
33
 *     $generator = new \Doctrine\ORM\Tools\EntityGenerator();
34
 *     $generator->setGenerateAnnotations(true);
35
 *     $generator->setGenerateStubMethods(true);
36
 *     $generator->setRegenerateEntityIfExists(false);
37
 *     $generator->setUpdateEntityIfExists(true);
38
 *     $generator->generate($classes, '/path/to/generate/entities');
39
 *
40
 *
41
 * @link    www.doctrine-project.org
42
 * @since   2.0
43
 * @author  Benjamin Eberlei <[email protected]>
44
 * @author  Guilherme Blanco <[email protected]>
45
 * @author  Jonathan Wage <[email protected]>
46
 * @author  Roman Borschel <[email protected]>
47
 */
48
class EntityGenerator
49
{
50
    /**
51
     * Specifies class fields should be protected.
52
     */
53
    const FIELD_VISIBLE_PROTECTED = 'protected';
54
55
    /**
56
     * Specifies class fields should be private.
57
     */
58
    const FIELD_VISIBLE_PRIVATE = 'private';
59
60
    /**
61
     * @var bool
62
     */
63
    protected $backupExisting = true;
64
65
    /**
66
     * The extension to use for written php files.
67
     *
68
     * @var string
69
     */
70
    protected $extension = '.php';
71
72
    /**
73
     * Whether or not the current ClassMetadataInfo instance is new or old.
74
     *
75
     * @var boolean
76
     */
77
    protected $isNew = true;
78
79
    /**
80
     * @var array
81
     */
82
    protected $staticReflection = [];
83
84
    /**
85
     * Number of spaces to use for indention in generated code.
86
     */
87
    protected $numSpaces = 4;
88
89
    /**
90
     * The actual spaces to use for indention.
91
     *
92
     * @var string
93
     */
94
    protected $spaces = '    ';
95
96
    /**
97
     * The class all generated entities should extend.
98
     *
99
     * @var string
100
     */
101
    protected $classToExtend;
102
103
    /**
104
     * Whether or not to generation annotations.
105
     *
106
     * @var boolean
107
     */
108
    protected $generateAnnotations = false;
109
110
    /**
111
     * @var string
112
     */
113
    protected $annotationsPrefix = '';
114
115
    /**
116
     * Whether or not to generate sub methods.
117
     *
118
     * @var boolean
119
     */
120
    protected $generateEntityStubMethods = false;
121
122
    /**
123
     * Whether or not to update the entity class if it exists already.
124
     *
125
     * @var boolean
126
     */
127
    protected $updateEntityIfExists = false;
128
129
    /**
130
     * Whether or not to re-generate entity class if it exists already.
131
     *
132
     * @var boolean
133
     */
134
    protected $regenerateEntityIfExists = false;
135
136
    /**
137
     * Visibility of the field
138
     *
139
     * @var string
140
     */
141
    protected $fieldVisibility = 'private';
142
143
    /**
144
     * Whether or not to make generated embeddables immutable.
145
     *
146
     * @var boolean.
147
     */
148
    protected $embeddablesImmutable = false;
149
150
    /**
151
     * Hash-map for handle types.
152
     *
153
     * @var array
154
     */
155
    protected $typeAlias = [
156
        Type::DATETIMETZ    => '\DateTime',
157
        Type::DATETIME      => '\DateTime',
158
        Type::DATE          => '\DateTime',
159
        Type::TIME          => '\DateTime',
160
        Type::OBJECT        => '\stdClass',
161
        Type::INTEGER       => 'int',
162
        Type::BIGINT        => 'int',
163
        Type::SMALLINT      => 'int',
164
        Type::TEXT          => 'string',
165
        Type::BLOB          => 'string',
166
        Type::DECIMAL       => 'string',
167
        Type::JSON_ARRAY    => 'array',
168
        Type::SIMPLE_ARRAY  => 'array',
169
        Type::BOOLEAN       => 'bool',
170
    ];
171
172
    /**
173
     * Hash-map to handle generator types string.
174
     *
175
     * @var array
176
     */
177
    protected static $generatorStrategyMap = [
178
        ClassMetadataInfo::GENERATOR_TYPE_AUTO      => 'AUTO',
179
        ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE  => 'SEQUENCE',
180
        ClassMetadataInfo::GENERATOR_TYPE_TABLE     => 'TABLE',
181
        ClassMetadataInfo::GENERATOR_TYPE_IDENTITY  => 'IDENTITY',
182
        ClassMetadataInfo::GENERATOR_TYPE_NONE      => 'NONE',
183
        ClassMetadataInfo::GENERATOR_TYPE_UUID      => 'UUID',
184
        ClassMetadataInfo::GENERATOR_TYPE_CUSTOM    => 'CUSTOM'
185
    ];
186
187
    /**
188
     * Hash-map to handle the change tracking policy string.
189
     *
190
     * @var array
191
     */
192
    protected static $changeTrackingPolicyMap = [
193
        ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT  => 'DEFERRED_IMPLICIT',
194
        ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT  => 'DEFERRED_EXPLICIT',
195
        ClassMetadataInfo::CHANGETRACKING_NOTIFY             => 'NOTIFY',
196
    ];
197
198
    /**
199
     * Hash-map to handle the inheritance type string.
200
     *
201
     * @var array
202
     */
203
    protected static $inheritanceTypeMap = [
204
        ClassMetadataInfo::INHERITANCE_TYPE_NONE            => 'NONE',
205
        ClassMetadataInfo::INHERITANCE_TYPE_JOINED          => 'JOINED',
206
        ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE    => 'SINGLE_TABLE',
207
        ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS',
208
    ];
209
210
    /**
211
     * @var string
212
     */
213
    protected static $classTemplate =
214
'<?php
215
216
<namespace>
217
<useStatement>
218
<entityAnnotation>
219
<entityClassName>
220
{
221
<entityBody>
222
}
223
';
224
225
    /**
226
     * @var string
227
     */
228
    protected static $getMethodTemplate =
229
'/**
230
 * <description>
231
 *
232
 * @return <variableType>
233
 */
234
public function <methodName>()
235
{
236
<spaces>return $this-><fieldName>;
237
}';
238
239
    /**
240
     * @var string
241
     */
242
    protected static $setMethodTemplate =
243
'/**
244
 * <description>
245
 *
246
 * @param <variableType> $<variableName>
247
 *
248
 * @return <entity>
249
 */
250
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
251
{
252
<spaces>$this-><fieldName> = $<variableName>;
253
254
<spaces>return $this;
255
}';
256
257
    /**
258
     * @var string
259
     */
260
    protected static $addMethodTemplate =
261
'/**
262
 * <description>
263
 *
264
 * @param <variableType> $<variableName>
265
 *
266
 * @return <entity>
267
 */
268
public function <methodName>(<methodTypeHint>$<variableName>)
269
{
270
<spaces>$this-><fieldName>[] = $<variableName>;
271
272
<spaces>return $this;
273
}';
274
275
    /**
276
     * @var string
277
     */
278
    protected static $removeMethodTemplate =
279
'/**
280
 * <description>
281
 *
282
 * @param <variableType> $<variableName>
283
 *
284
 * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
285
 */
286
public function <methodName>(<methodTypeHint>$<variableName>)
287
{
288
<spaces>return $this-><fieldName>->removeElement($<variableName>);
289
}';
290
291
    /**
292
     * @var string
293
     */
294
    protected static $lifecycleCallbackMethodTemplate =
295
'/**
296
 * @<name>
297
 */
298
public function <methodName>()
299
{
300
<spaces>// Add your code here
301
}';
302
303
    /**
304
     * @var string
305
     */
306
    protected static $constructorMethodTemplate =
307
'/**
308
 * Constructor
309
 */
310
public function __construct()
311
{
312
<spaces><collections>
313
}
314
';
315
316
    /**
317
     * @var string
318
     */
319
    protected static $embeddableConstructorMethodTemplate =
320
'/**
321
 * Constructor
322
 *
323
 * <paramTags>
324
 */
325
public function __construct(<params>)
326
{
327
<spaces><fields>
328
}
329
';
330
331
    /**
332
     * Constructor.
333
     */
334 40
    public function __construct()
335
    {
336 40
        if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
337 40
            $this->annotationsPrefix = 'ORM\\';
338
        }
339 40
    }
340
341
    /**
342
     * Generates and writes entity classes for the given array of ClassMetadataInfo instances.
343
     *
344
     * @param array  $metadatas
345
     * @param string $outputDirectory
346
     *
347
     * @return void
348
     */
349
    public function generate(array $metadatas, $outputDirectory)
350
    {
351
        foreach ($metadatas as $metadata) {
352
            $this->writeEntityClass($metadata, $outputDirectory);
353
        }
354
    }
355
356
    /**
357
     * Generates and writes entity class to disk for the given ClassMetadataInfo instance.
358
     *
359
     * @param ClassMetadataInfo $metadata
360
     * @param string            $outputDirectory
361
     *
362
     * @return void
363
     *
364
     * @throws \RuntimeException
365
     */
366 31
    public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
367
    {
368 31
        $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension;
369 31
        $dir = dirname($path);
370
371 31
        if ( ! is_dir($dir)) {
372 2
            mkdir($dir, 0775, true);
373
        }
374
375 31
        $this->isNew = ! file_exists($path) || $this->regenerateEntityIfExists;
376
377 31
        if ( ! $this->isNew) {
378 3
            $this->parseTokensInEntityFile(file_get_contents($path));
379
        } else {
380 30
            $this->staticReflection[$metadata->name] = ['properties' => [], 'methods' => []];
381
        }
382
383 31
        if ($this->backupExisting && file_exists($path)) {
384 3
            $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
385 3
            if (!copy($path, $backupPath)) {
386
                throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
387
            }
388
        }
389
390
        // If entity doesn't exist or we're re-generating the entities entirely
391 31
        if ($this->isNew) {
392 30
            file_put_contents($path, $this->generateEntityClass($metadata));
393
        // If entity exists and we're allowed to update the entity class
394 3
        } elseif ($this->updateEntityIfExists) {
395 3
            file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
396
        }
397 31
        chmod($path, 0664);
398 31
    }
399
400
    /**
401
     * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance.
402
     *
403
     * @param ClassMetadataInfo $metadata
404
     *
405
     * @return string
406
     */
407 31
    public function generateEntityClass(ClassMetadataInfo $metadata)
408
    {
409
        $placeHolders = [
410 31
            '<namespace>',
411
            '<useStatement>',
412
            '<entityAnnotation>',
413
            '<entityClassName>',
414
            '<entityBody>'
415
        ];
416
417
        $replacements = [
418 31
            $this->generateEntityNamespace($metadata),
419 31
            $this->generateEntityUse(),
420 31
            $this->generateEntityDocBlock($metadata),
421 31
            $this->generateEntityClassName($metadata),
422 31
            $this->generateEntityBody($metadata)
423
        ];
424
425 31
        $code = str_replace($placeHolders, $replacements, static::$classTemplate);
426
427 31
        return str_replace('<spaces>', $this->spaces, $code);
428
    }
429
430
    /**
431
     * Generates the updated code for the given ClassMetadataInfo and entity at path.
432
     *
433
     * @param ClassMetadataInfo $metadata
434
     * @param string            $path
435
     *
436
     * @return string
437
     */
438 3
    public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
439
    {
440 3
        $currentCode = file_get_contents($path);
441
442 3
        $body = $this->generateEntityBody($metadata);
443 3
        $body = str_replace('<spaces>', $this->spaces, $body);
444 3
        $last = strrpos($currentCode, '}');
445
446 3
        return substr($currentCode, 0, $last) . $body . ($body ? "\n" : '') . "}\n";
447
    }
448
449
    /**
450
     * Sets the number of spaces the exported class should have.
451
     *
452
     * @param integer $numSpaces
453
     *
454
     * @return void
455
     */
456
    public function setNumSpaces($numSpaces)
457
    {
458
        $this->spaces = str_repeat(' ', $numSpaces);
459
        $this->numSpaces = $numSpaces;
460
    }
461
462
    /**
463
     * Sets the extension to use when writing php files to disk.
464
     *
465
     * @param string $extension
466
     *
467
     * @return void
468
     */
469
    public function setExtension($extension)
470
    {
471
        $this->extension = $extension;
472
    }
473
474
    /**
475
     * Sets the name of the class the generated classes should extend from.
476
     *
477
     * @param string $classToExtend
478
     *
479
     * @return void
480
     */
481 1
    public function setClassToExtend($classToExtend)
482
    {
483 1
        $this->classToExtend = $classToExtend;
484 1
    }
485
486
    /**
487
     * Sets whether or not to generate annotations for the entity.
488
     *
489
     * @param bool $bool
490
     *
491
     * @return void
492
     */
493 40
    public function setGenerateAnnotations($bool)
494
    {
495 40
        $this->generateAnnotations = $bool;
496 40
    }
497
498
    /**
499
     * Sets the class fields visibility for the entity (can either be private or protected).
500
     *
501
     * @param bool $visibility
502
     *
503
     * @return void
504
     *
505
     * @throws \InvalidArgumentException
506
     */
507 39
    public function setFieldVisibility($visibility)
508
    {
509 39
        if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) {
510
            throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility);
511
        }
512
513 39
        $this->fieldVisibility = $visibility;
514 39
    }
515
516
    /**
517
     * Sets whether or not to generate immutable embeddables.
518
     *
519
     * @param boolean $embeddablesImmutable
520
     */
521 1
    public function setEmbeddablesImmutable($embeddablesImmutable)
522
    {
523 1
        $this->embeddablesImmutable = (boolean) $embeddablesImmutable;
524 1
    }
525
526
    /**
527
     * Sets an annotation prefix.
528
     *
529
     * @param string $prefix
530
     *
531
     * @return void
532
     */
533 40
    public function setAnnotationPrefix($prefix)
534
    {
535 40
        $this->annotationsPrefix = $prefix;
536 40
    }
537
538
    /**
539
     * Sets whether or not to try and update the entity if it already exists.
540
     *
541
     * @param bool $bool
542
     *
543
     * @return void
544
     */
545 40
    public function setUpdateEntityIfExists($bool)
546
    {
547 40
        $this->updateEntityIfExists = $bool;
548 40
    }
549
550
    /**
551
     * Sets whether or not to regenerate the entity if it exists.
552
     *
553
     * @param bool $bool
554
     *
555
     * @return void
556
     */
557 40
    public function setRegenerateEntityIfExists($bool)
558
    {
559 40
        $this->regenerateEntityIfExists = $bool;
560 40
    }
561
562
    /**
563
     * Sets whether or not to generate stub methods for the entity.
564
     *
565
     * @param bool $bool
566
     *
567
     * @return void
568
     */
569 40
    public function setGenerateStubMethods($bool)
570
    {
571 40
        $this->generateEntityStubMethods = $bool;
572 40
    }
573
574
    /**
575
     * Should an existing entity be backed up if it already exists?
576
     *
577
     * @param bool $bool
578
     *
579
     * @return void
580
     */
581 1
    public function setBackupExisting($bool)
582
    {
583 1
        $this->backupExisting = $bool;
584 1
    }
585
586
    /**
587
     * @param string $type
588
     *
589
     * @return string
590
     */
591 31
    protected function getType($type)
592
    {
593 31
        if (isset($this->typeAlias[$type])) {
594 30
            return $this->typeAlias[$type];
595
        }
596
597 20
        return $type;
598
    }
599
600
    /**
601
     * @param ClassMetadataInfo $metadata
602
     *
603
     * @return string
604
     */
605 31
    protected function generateEntityNamespace(ClassMetadataInfo $metadata)
606
    {
607 31
        if (! $this->hasNamespace($metadata)) {
608 31
            return '';
609
        }
610 2
611
        return 'namespace ' . $this->getNamespace($metadata) .';';
612 31
    }
613
614 31
    /**
615 31
     * @return string
616
     */
617
    protected function generateEntityUse()
618
    {
619
        if (! $this->generateAnnotations) {
620
            return '';
621
        }
622
623
        return "\n".'use Doctrine\ORM\Mapping as ORM;'."\n";
624
    }
625
626 31
    /**
627
     * @param ClassMetadataInfo $metadata
628 31
     *
629 31
     * @return string
630
     */
631
    protected function generateEntityClassName(ClassMetadataInfo $metadata)
632
    {
633
        return 'class ' . $this->getClassName($metadata) .
634
            ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
635
    }
636
637 32
    /**
638
     * @param ClassMetadataInfo $metadata
639 32
     *
640 32
     * @return string
641 32
     */
642 32
    protected function generateEntityBody(ClassMetadataInfo $metadata)
643 32
    {
644
        $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
645 32
        $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata);
646
        $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
647 32
        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
648 29
        $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
649
650
        $code = [];
651 32
652 10
        if ($fieldMappingProperties) {
653
            $code[] = $fieldMappingProperties;
654
        }
655 32
656 11
        if ($embeddedProperties) {
657
            $code[] = $embeddedProperties;
658
        }
659 32
660
        if ($associationMappingProperties) {
661 32
            $code[] = $associationMappingProperties;
662 30
        }
663
664
        $code[] = $this->generateEntityConstructor($metadata);
665 32
666 10
        if ($stubMethods) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stubMethods of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
667
            $code[] = $stubMethods;
668
        }
669 32
670
        if ($lifecycleCallbackMethods) {
671
            $code[] = $lifecycleCallbackMethods;
672
        }
673
674
        return implode("\n", $code);
675
    }
676
677 32
    /**
678
     * @param ClassMetadataInfo $metadata
679 32
     *
680 2
     * @return string
681
     */
682
    protected function generateEntityConstructor(ClassMetadataInfo $metadata)
683 32
    {
684 1
        if ($this->hasMethod('__construct', $metadata)) {
685
            return '';
686
        }
687 31
688
        if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) {
689 31
            return $this->generateEmbeddableConstructor($metadata);
690 13
        }
691 13
692
        $collections = [];
693
694
        foreach ($metadata->associationMappings as $mapping) {
695 31
            if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
696 11
                $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
697
            }
698
        }
699 27
700
        if ($collections) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collections of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
701
            return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate));
702
        }
703
704
        return '';
705
    }
706
707 1
    /**
708
     * @param ClassMetadataInfo $metadata
709 1
     *
710 1
     * @return string
711 1
     */
712 1
    private function generateEmbeddableConstructor(ClassMetadataInfo $metadata)
713
    {
714
        $paramTypes = [];
715 1
        $paramVariables = [];
716 1
        $params = [];
717
        $fields = [];
718 1
719 1
        // Resort fields to put optional fields at the end of the method signature.
720 1
        $requiredFields = [];
721
        $optionalFields = [];
722 1
723
        foreach ($metadata->fieldMappings as $fieldMapping) {
724
            if (empty($fieldMapping['nullable'])) {
725 1
                $requiredFields[] = $fieldMapping;
726
727
                continue;
728 1
            }
729
730 1
            $optionalFields[] = $fieldMapping;
731 1
        }
732 1
733
        $fieldMappings = array_merge($requiredFields, $optionalFields);
734 1
735 1
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
736 1
            $paramType = '\\' . ltrim($embeddedClass['class'], '\\');
737 1
            $paramVariable = '$' . $fieldName;
738
739
            $paramTypes[] = $paramType;
740 1
            $paramVariables[] = $paramVariable;
741 1
            $params[] = $paramType . ' ' . $paramVariable;
742 1
            $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';';
743
        }
744
745
        foreach ($fieldMappings as $fieldMapping) {
746
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) {
747 1
                continue;
748 1
            }
749 1
750
            $paramTypes[] = $this->getType($fieldMapping['type']) . (!empty($fieldMapping['nullable']) ? '|null' : '');
751 1
            $param = '$' . $fieldMapping['fieldName'];
752 1
            $paramVariables[] = $param;
753
754
            if ($fieldMapping['type'] === 'datetime') {
755 1
                $param = $this->getType($fieldMapping['type']) . ' ' . $param;
756 1
            }
757
758
            if (!empty($fieldMapping['nullable'])) {
759 1
                $param .= ' = null';
760
            }
761 1
762
            $params[] = $param;
763
764 1
            $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';';
765 1
        }
766 1
767 1
        $maxParamTypeLength = max(array_map('strlen', $paramTypes));
768 1
        $paramTags = array_map(
769 1
            function ($type, $variable) use ($maxParamTypeLength) {
770 1
                return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable;
771
            },
772
            $paramTypes,
773
            $paramVariables
774 1
        );
775 1
776 1
        // Generate multi line constructor if the signature exceeds 120 characters.
777
        if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) {
778 1
            $delimiter = "\n" . $this->spaces;
779
            $params = $delimiter . implode(',' . $delimiter, $params) . "\n";
780
        } else {
781
            $params = implode(', ', $params);
782 1
        }
783 1
784 1
        $replacements = [
785
            '<paramTags>' => implode("\n * ", $paramTags),
786
            '<params>'    => $params,
787 1
            '<fields>'    => implode("\n" . $this->spaces, $fields),
788 1
        ];
789 1
790 1
        $constructor = str_replace(
791
            array_keys($replacements),
792
            array_values($replacements),
793 1
            static::$embeddableConstructorMethodTemplate
794
        );
795
796
        return $this->prefixCodeWithSpaces($constructor);
797
    }
798
799
    /**
800
     * @todo this won't work if there is a namespace in brackets and a class outside of it.
801
     *
802
     * @param string $src
803 8
     *
804
     * @return void
805 8
     */
806 8
    protected function parseTokensInEntityFile($src)
807 8
    {
808 8
        $tokens = token_get_all($src);
809
        $tokensCount = count($tokens);
810 8
        $lastSeenNamespace = '';
811 8
        $lastSeenClass = false;
812
813 8
        $inNamespace = false;
814 8
        $inClass = false;
815 8
816 8
        for ($i = 0; $i < $tokensCount; $i++) {
817
            $token = $tokens[$i];
818
            if (in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
819 8
                continue;
820 8
            }
821 8
822 8
            if ($inNamespace) {
823 8
                if (in_array($token[0], [T_NS_SEPARATOR, T_STRING], true)) {
824
                    $lastSeenNamespace .= $token[1];
825
                } elseif (is_string($token) && in_array($token, [';', '{'], true)) {
826
                    $inNamespace = false;
827 8
                }
828 8
            }
829 8
830 8
            if ($inClass) {
831 8
                $inClass = false;
832
                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
833
                $this->staticReflection[$lastSeenClass]['properties'] = [];
834 8
                $this->staticReflection[$lastSeenClass]['methods'] = [];
835 8
            }
836 8
837 8
            if (T_NAMESPACE === $token[0]) {
838 8
                $lastSeenNamespace = '';
839 8
                $inNamespace = true;
840 3
            } elseif (T_CLASS === $token[0] && T_DOUBLE_COLON !== $tokens[$i-1][0]) {
841 3
                $inClass = true;
842
            } elseif (T_FUNCTION === $token[0]) {
843 3
                if (T_STRING === $tokens[$i+2][0]) {
844
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]);
845 8
                } elseif ($tokens[$i+2] == '&' && T_STRING === $tokens[$i+3][0]) {
846 4
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]);
847
                }
848
            } elseif (in_array($token[0], [T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED], true) && T_FUNCTION !== $tokens[$i+2][0]) {
849 8
                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
850
            }
851
        }
852
    }
853
854
    /**
855
     * @param string            $property
856
     * @param ClassMetadataInfo $metadata
857 31
     *
858
     * @return bool
859 31
     */
860 View Code Duplication
    protected function hasProperty($property, ClassMetadataInfo $metadata)
861 2
    {
862 2
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
863 1
            // don't generate property if its already on the base class.
864
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
865
            if ($reflClass->hasProperty($property)) {
866
                return true;
867
            }
868 30
        }
869 2
870 2
        // check traits for existing property
871
        foreach ($this->getTraits($metadata) as $trait) {
872
            if ($trait->hasProperty($property)) {
873
                return true;
874
            }
875 30
        }
876 30
877
        return (
878
            isset($this->staticReflection[$metadata->name]) &&
879
            in_array($property, $this->staticReflection[$metadata->name]['properties'], true)
880
        );
881
    }
882
883
    /**
884
     * @param string            $method
885
     * @param ClassMetadataInfo $metadata
886 32
     *
887
     * @return bool
888 32
     */
889 View Code Duplication
    protected function hasMethod($method, ClassMetadataInfo $metadata)
890 2
    {
891
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
892 2
            // don't generate method if its already on the base class.
893 1
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
894
895
            if ($reflClass->hasMethod($method)) {
896
                return true;
897
            }
898 32
        }
899 2
900 2
        // check traits for existing method
901
        foreach ($this->getTraits($metadata) as $trait) {
902
            if ($trait->hasMethod($method)) {
903
                return true;
904
            }
905 32
        }
906 32
907
        return (
908
            isset($this->staticReflection[$metadata->name]) &&
909
            in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods'], true)
910
        );
911
    }
912
913
    /**
914
     * @param ClassMetadataInfo $metadata
915 32
     *
916
     * @return array
917 32
     *
918 26
     * @throws \ReflectionException
919
     */
920
    protected function getTraits(ClassMetadataInfo $metadata)
921 7
    {
922 1
        if (! ($metadata->reflClass !== null || class_exists($metadata->name))) {
923 7
            return [];
924
        }
925 7
926
        $reflClass = $metadata->reflClass ?? new \ReflectionClass($metadata->name);
927 7
928 7
        $traits = [];
929
930 7
        while ($reflClass !== false) {
931
            $traits = array_merge($traits, $reflClass->getTraits());
932
933 7
            $reflClass = $reflClass->getParentClass();
934
        }
935
936
        return $traits;
937
    }
938
939
    /**
940
     * @param ClassMetadataInfo $metadata
941 31
     *
942
     * @return bool
943 31
     */
944
    protected function hasNamespace(ClassMetadataInfo $metadata)
945
    {
946
        return (bool) strpos($metadata->name, '\\');
947
    }
948
949 32
    /**
950
     * @return bool
951 32
     */
952
    protected function extendsClass()
953
    {
954
        return (bool) $this->classToExtend;
955
    }
956
957 2
    /**
958
     * @return string
959 2
     */
960
    protected function getClassToExtend()
961
    {
962
        return $this->classToExtend;
963
    }
964
965 1
    /**
966
     * @return string
967 1
     */
968
    protected function getClassToExtendName()
969 1
    {
970
        $refl = new \ReflectionClass($this->getClassToExtend());
971
972
        return '\\' . $refl->getName();
0 ignored issues
show
Bug introduced by
Consider using $refl->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
973
    }
974
975
    /**
976
     * @param ClassMetadataInfo $metadata
977 32
     *
978
     * @return string
979 32
     */
980 32
    protected function getClassName(ClassMetadataInfo $metadata)
981
    {
982
        return ($pos = strrpos($metadata->name, '\\'))
983
            ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
984
    }
985
986
    /**
987
     * @param ClassMetadataInfo $metadata
988 31
     *
989
     * @return string
990 31
     */
991
    protected function getNamespace(ClassMetadataInfo $metadata)
992
    {
993
        return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
994
    }
995
996
    /**
997
     * @param ClassMetadataInfo $metadata
998 31
     *
999
     * @return string
1000 31
     */
1001 31
    protected function generateEntityDocBlock(ClassMetadataInfo $metadata)
1002 31
    {
1003
        $lines = [];
1004 31
        $lines[] = '/**';
1005 31
        $lines[] = ' * ' . $this->getClassName($metadata);
1006
1007
        if ($this->generateAnnotations) {
1008 31
            $lines[] = ' *';
1009
1010
            $methods = [
1011
                'generateTableAnnotation',
1012
                'generateInheritanceAnnotation',
1013
                'generateDiscriminatorColumnAnnotation',
1014
                'generateDiscriminatorMapAnnotation',
1015 31
                'generateEntityAnnotation',
1016 31
                'generateEntityListenerAnnotation',
1017 31
            ];
1018
1019
            foreach ($methods as $method) {
1020
                if ($code = $this->$method($metadata)) {
1021 31
                    $lines[] = ' * ' . $code;
1022 10
                }
1023
            }
1024
1025
            if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->lifecycleCallbacks of type array[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1026 31
                $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
1027
            }
1028 31
        }
1029
1030
        $lines[] = ' */';
1031
1032
        return implode("\n", $lines);
1033
    }
1034
1035
    /**
1036 31
     * @param ClassMetadataInfo $metadata
1037
     *
1038 31
     * @return string
1039
     */
1040 31
    protected function generateEntityAnnotation(ClassMetadataInfo $metadata)
1041 11
    {
1042
        $prefix = '@' . $this->annotationsPrefix;
1043
1044 27
        if ($metadata->isEmbeddedClass) {
1045 11
            return $prefix . 'Embeddable';
1046 27
        }
1047
1048 27
        $customRepository = $metadata->customRepositoryClassName
1049
            ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")'
1050
            : '';
1051
1052
        return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository;
1053
    }
1054
1055
    /**
1056 31
     * @param ClassMetadataInfo $metadata
1057
     *
1058 31
     * @return string
1059 11
     */
1060
    protected function generateTableAnnotation(ClassMetadataInfo $metadata)
1061
    {
1062 27
        if ($metadata->isEmbeddedClass) {
1063
            return '';
1064 27
        }
1065
1066
        $table = [];
1067
1068 27 View Code Duplication
        if (isset($metadata->table['schema'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1069 24
            $table[] = 'schema="' . $metadata->table['schema'] . '"';
1070
        }
1071
1072 27 View Code Duplication
        if (isset($metadata->table['name'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1073 1
            $table[] = 'name="' . $metadata->table['name'] . '"';
1074
        }
1075
1076 27
        if (isset($metadata->table['options']) && $metadata->table['options']) {
1077 9
            $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}';
1078 9
        }
1079
1080 View Code Duplication
        if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1081 27
            $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
1082 9
            $table[] = 'uniqueConstraints={' . $constraints . '}';
1083 9
        }
1084
1085 View Code Duplication
        if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1086 27
            $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
1087
            $table[] = 'indexes={' . $constraints . '}';
1088
        }
1089
1090
        return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
1091
    }
1092
1093
    /**
1094
     * @param string $constraintName
1095 9
     * @param array  $constraints
1096
     *
1097 9
     * @return string
1098 9
     */
1099 9
    protected function generateTableConstraints($constraintName, array $constraints)
1100 9
    {
1101 9
        $annotations = [];
1102
        foreach ($constraints as $name => $constraint) {
1103 9
            $columns = [];
1104
            foreach ($constraint['columns'] as $column) {
1105
                $columns[] = '"' . $column . '"';
1106 9
            }
1107
            $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
1108
        }
1109
1110
        return implode(', ', $annotations);
1111
    }
1112
1113
    /**
1114 31
     * @param ClassMetadataInfo $metadata
1115
     *
1116 31
     * @return string
1117
     */
1118
    protected function generateInheritanceAnnotation(ClassMetadataInfo $metadata)
1119 31
    {
1120
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1121
            return '';
1122
        }
1123
1124
        return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
1125
    }
1126 31
1127
    /**
1128 31
     * @param ClassMetadataInfo $metadata
1129
     *
1130
     * @return string
1131
     */
1132
    protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $metadata)
1133
    {
1134
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1135
            return '';
1136 31
        }
1137
1138
        $discrColumn = $metadata->discriminatorColumn;
1139
        $columnDefinition = 'name="' . $discrColumn['name']
1140
            . '", type="' . $discrColumn['type']
1141
            . '", length=' . $discrColumn['length'];
1142
1143 31
        return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
1144
    }
1145 31
1146
    /**
1147
     * @param ClassMetadataInfo $metadata
1148
     *
1149
     * @return string
1150
     */
1151
    protected function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata)
1152
    {
1153
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1154 31
            return null;
1155
        }
1156
1157
        $inheritanceClassMap = [];
1158
1159
        foreach ($metadata->discriminatorMap as $type => $class) {
1160
            $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
1161 31
        }
1162
1163 31
        return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
1164
    }
1165 31
1166 30
    /**
1167 30
     * @param ClassMetadataInfo $metadata
1168
     *
1169
     * @return string
1170
     */
1171
    protected function generateEntityStubMethods(ClassMetadataInfo $metadata)
1172 30
    {
1173
        $methods = [];
1174 30
1175 26
        foreach ($metadata->fieldMappings as $fieldMapping) {
1176 30
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) {
1177 30
                continue;
1178 30
            }
1179
1180 27
            $nullableField = $this->nullableFieldExpression($fieldMapping);
1181
1182
            if ((!$metadata->isEmbeddedClass || !$this->embeddablesImmutable)
1183 30
                && (!isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_NONE)
1184 30
                && $code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)
1185
            ) {
1186
                $methods[] = $code;
1187
            }
1188 31
1189 10
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)) {
1190 1
                $methods[] = $code;
1191
            }
1192
        }
1193 10
1194 9
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1195 9
            if (isset($embeddedClass['declaredField'])) {
1196
                continue;
1197
            }
1198
1199 10
            if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) {
1200 10
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) {
1201
                    $methods[] = $code;
1202
                }
1203
            }
1204 31
1205 12
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) {
1206 11
                $methods[] = $code;
1207 11
            }
1208 9
        }
1209
1210 11
        foreach ($metadata->associationMappings as $associationMapping) {
1211 11
            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
1212
                $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
1213 10
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1214 10
                    $methods[] = $code;
1215 10
                }
1216
                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1217 10
                    $methods[] = $code;
1218 10
                }
1219
            } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1220 10 View Code Duplication
                if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1221 12
                    $methods[] = $code;
1222
                }
1223 View Code Duplication
                if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1224
                    $methods[] = $code;
1225
                }
1226 31 View Code Duplication
                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], Collection::class)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1227
                    $methods[] = $code;
1228
                }
1229
            }
1230
        }
1231
1232
        return implode("\n\n", $methods);
1233
    }
1234 11
1235
    /**
1236 11
     * @param array $associationMapping
1237
     *
1238
     * @return bool
1239
     */
1240 11
    protected function isAssociationIsNullable(array $associationMapping)
1241 2
    {
1242
        if (isset($associationMapping['id']) && $associationMapping['id']) {
1243
            return false;
1244 9
        }
1245
1246
        if (isset($associationMapping['joinColumns'])) {
1247 11
            $joinColumns = $associationMapping['joinColumns'];
1248 2
        } else {
1249 2
            //@todo there is no way to retrieve targetEntity metadata
1250
            $joinColumns = [];
1251
        }
1252
1253 11
        foreach ($joinColumns as $joinColumn) {
1254
            if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
1255
                return false;
1256
            }
1257
        }
1258
1259
        return true;
1260
    }
1261 32
1262
    /**
1263 32
     * @param ClassMetadataInfo $metadata
1264 29
     *
1265
     * @return string
1266
     */
1267 10
    protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
1268
    {
1269 10
        if (empty($metadata->lifecycleCallbacks)) {
1270 10
            return '';
1271 10
        }
1272
1273
        $methods = [];
1274
1275 10
        foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
1276
            foreach ($callbacks as $callback) {
1277
                $methods[] = $this->generateLifecycleCallbackMethod($name, $callback, $metadata);
1278
            }
1279
        }
1280
1281
        return implode("\n\n", array_filter($methods));
1282
    }
1283 32
1284
    /**
1285 32
     * @param ClassMetadataInfo $metadata
1286
     *
1287 32
     * @return string
1288 13
     */
1289 4
    protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
1290
    {
1291
        $lines = [];
1292 11
1293 11
        foreach ($metadata->associationMappings as $associationMapping) {
1294 11
            if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
1295
                continue;
1296
            }
1297 32
1298
            $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
1299
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
1300
                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
1301
        }
1302
1303
        return implode("\n", $lines);
1304
    }
1305 32
1306
    /**
1307 32
     * @param ClassMetadataInfo $metadata
1308
     *
1309 32
     * @return string
1310 31
     */
1311 30
    protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
1312
    {
1313 29
        $lines = [];
1314 31
1315
        foreach ($metadata->fieldMappings as $fieldMapping) {
1316
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']]) ||
1317 4
                $this->hasProperty($fieldMapping['fieldName'], $metadata) ||
1318
                $metadata->isInheritedField($fieldMapping['fieldName'])
1319
            ) {
1320 29
                continue;
1321 29
            }
1322 29
1323
            $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
1324
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
1325 32
                     . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n";
1326
        }
1327
1328
        return implode("\n", $lines);
1329
    }
1330
1331
    /**
1332
     * @param ClassMetadataInfo $metadata
1333 32
     *
1334
     * @return string
1335 32
     */
1336
    protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata)
1337 32
    {
1338 10
        $lines = [];
1339 2
1340
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1341
            if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) {
1342 10
                continue;
1343 10
            }
1344
1345
            $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass);
1346 32
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n";
1347
        }
1348
1349
        return implode("\n", $lines);
1350
    }
1351
1352
    /**
1353
     * @param ClassMetadataInfo $metadata
1354
     * @param string            $type
1355
     * @param string            $fieldName
1356
     * @param string|null       $typeHint
1357
     * @param string|null       $defaultValue
1358 30
     *
1359
     * @return string
1360 30
     */
1361 30
    protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
1362 30
    {
1363 10
        $methodName = $type . Inflector::classify($fieldName);
1364 10
        $variableName = Inflector::camelize($fieldName);
1365
        if (in_array($type, ["add", "remove"])) {
1366
            $methodName = Inflector::singularize($methodName);
1367 30
            $variableName = Inflector::singularize($variableName);
1368 5
        }
1369
1370 30
        if ($this->hasMethod($methodName, $metadata)) {
1371
            return '';
1372 30
        }
1373 30
        $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName);
1374
1375 30
        $var = sprintf('%sMethodTemplate', $type);
1376 30
        $template = static::$$var;
1377 30
1378
        $methodTypeHint = null;
1379 30
        $types          = Type::getTypesMap();
1380 14
        $variableType   = $typeHint ? $this->getType($typeHint) : null;
1381 14
1382
        if ($typeHint && ! isset($types[$typeHint])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeHint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1383
            $variableType   =  '\\' . ltrim($variableType, '\\');
1384
            $methodTypeHint =  '\\' . $typeHint . ' ';
1385 30
        }
1386 30
1387 30
        $replacements = [
1388 30
          '<description>'       => ucfirst($type) . ' ' . $variableName . '.',
1389 30
          '<methodTypeHint>'    => $methodTypeHint,
1390 30
          '<variableType>'      => $variableType . (null !== $defaultValue ? ('|' . $defaultValue) : ''),
1391 30
          '<variableName>'      => $variableName,
1392 30
          '<methodName>'        => $methodName,
1393
          '<fieldName>'         => $fieldName,
1394
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = ' . $defaultValue) : '',
1395 30
          '<entity>'            => $this->getClassName($metadata)
1396 30
        ];
1397 30
1398 30
        $method = str_replace(
1399
            array_keys($replacements),
1400
            array_values($replacements),
1401 30
            $template
1402
        );
1403
1404
        return $this->prefixCodeWithSpaces($method);
1405
    }
1406
1407
    /**
1408
     * @param string            $name
1409
     * @param string            $methodName
1410
     * @param ClassMetadataInfo $metadata
1411 10
     *
1412
     * @return string
1413 10
     */
1414 2
    protected function generateLifecycleCallbackMethod($name, $methodName, ClassMetadataInfo $metadata)
1415
    {
1416 10
        if ($this->hasMethod($methodName, $metadata)) {
1417
            return '';
1418
        }
1419 10
1420 10
        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
1421
1422
        $replacements = [
1423 10
            '<name>'        => $this->annotationsPrefix . ucfirst($name),
1424 10
            '<methodName>'  => $methodName,
1425 10
        ];
1426 10
1427
        $method = str_replace(
1428
            array_keys($replacements),
1429 10
            array_values($replacements),
1430
            static::$lifecycleCallbackMethodTemplate
1431
        );
1432
1433
        return $this->prefixCodeWithSpaces($method);
1434
    }
1435
1436
    /**
1437 11
     * @param array $joinColumn
1438
     *
1439 11
     * @return string
1440
     */
1441 11
    protected function generateJoinColumnAnnotation(array $joinColumn)
1442 11
    {
1443
        $joinColumnAnnot = [];
1444
1445 11
        if (isset($joinColumn['name'])) {
1446 11
            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
1447
        }
1448
1449 11
        if (isset($joinColumn['referencedColumnName'])) {
1450 1
            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
1451
        }
1452
1453 11
        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
1454 1
            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
1455
        }
1456
1457 11
        if (isset($joinColumn['nullable'])) {
1458 1
            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
1459
        }
1460
1461 11
        if (isset($joinColumn['onDelete'])) {
1462 1
            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
1463
        }
1464
1465 11
        if (isset($joinColumn['columnDefinition'])) {
1466
            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
1467
        }
1468
1469
        return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
1470
    }
1471
1472
    /**
1473
     * @param array             $associationMapping
1474 11
     * @param ClassMetadataInfo $metadata
1475
     *
1476 11
     * @return string
1477 11
     */
1478
    protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
1479 11
    {
1480 11
        $lines = [];
1481
        $lines[] = $this->spaces . '/**';
1482 10
1483
        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1484
            $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection';
1485 11
        } else {
1486 11
            $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\');
1487
        }
1488 11
1489
        if ($this->generateAnnotations) {
1490
            $lines[] = $this->spaces . ' *';
1491
1492
            if (isset($associationMapping['id']) && $associationMapping['id']) {
1493
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1494
1495 View Code Duplication
                if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1496 11
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1497 11
                }
1498 11
            }
1499 10
1500 10
            $type = null;
1501 11
            switch ($associationMapping['type']) {
1502 1
                case ClassMetadataInfo::ONE_TO_ONE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1503 1
                    $type = 'OneToOne';
1504 11
                    break;
1505 1
                case ClassMetadataInfo::MANY_TO_ONE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1506 1
                    $type = 'ManyToOne';
1507 11
                    break;
1508 11
                case ClassMetadataInfo::ONE_TO_MANY:
1509 11
                    $type = 'OneToMany';
1510
                    break;
1511 11
                case ClassMetadataInfo::MANY_TO_MANY:
1512
                    $type = 'ManyToMany';
1513 11
                    break;
1514 11
            }
1515
            $typeOptions = [];
1516
1517 11
            if (isset($associationMapping['targetEntity'])) {
1518 1
                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
1519
            }
1520
1521 11
            if (isset($associationMapping['inversedBy'])) {
1522 10
                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
1523
            }
1524
1525 11
            if (isset($associationMapping['mappedBy'])) {
1526 1
                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
1527
            }
1528 1
1529 1
            if ($associationMapping['cascade']) {
1530 1
                $cascades = [];
1531 1
1532 1
                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
1533
                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
1534 1
                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
1535 1
                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
1536
                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
1537
1538 1
                if (count($cascades) === 5) {
1539
                    $cascades = ['"all"'];
1540
                }
1541 11
1542 1
                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
1543
            }
1544
1545 11
            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
1546
                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
1547 10
            }
1548
1549
            if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) {
1550
                $fetchMap = [
1551 10
                    ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY',
1552
                    ClassMetadataInfo::FETCH_EAGER      => 'EAGER',
1553
                ];
1554 11
1555
                $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"';
1556 11
            }
1557 1
1558
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
1559 1
1560
            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
1561 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
1562 1
1563 1
                $joinColumnsLines = [];
1564
1565 View Code Duplication
                foreach ($associationMapping['joinColumns'] as $joinColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1566
                    if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
1567 1
                        $joinColumnsLines[] = $this->spaces . ' *   ' . $joinColumnAnnot;
1568 1
                    }
1569
                }
1570
1571 11
                $lines[] = implode(",\n", $joinColumnsLines);
1572 11
                $lines[] = $this->spaces . ' * })';
1573 11
            }
1574
1575 11
            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
1576
                $joinTable = [];
1577
                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
1578
1579 11
                if (isset($associationMapping['joinTable']['schema'])) {
1580 11
                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
1581
                }
1582 11
1583
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
1584 11
                $lines[] = $this->spaces . ' *   joinColumns={';
1585 11
1586
                $joinColumnsLines = [];
1587
1588 11 View Code Duplication
                foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1589 11
                    $joinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1590 11
                }
1591
1592 11
                $lines[] = implode(",". PHP_EOL, $joinColumnsLines);
1593
                $lines[] = $this->spaces . ' *   },';
1594 11
                $lines[] = $this->spaces . ' *   inverseJoinColumns={';
1595 11
1596
                $inverseJoinColumnsLines = [];
1597
1598 11
                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
1599 11
                    $inverseJoinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1600 11
                }
1601
1602
                $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines);
1603 11
                $lines[] = $this->spaces . ' *   }';
1604 1
                $lines[] = $this->spaces . ' * )';
1605
            }
1606 1
1607 1
            if (isset($associationMapping['orderBy'])) {
1608
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
1609
1610 1
                foreach ($associationMapping['orderBy'] as $name => $direction) {
1611 1
                    $lines[] = $this->spaces . ' *     "' . $name . '"="' . $direction . '",';
1612
                }
1613
1614
                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
1615 11
                $lines[] = $this->spaces . ' * })';
1616
            }
1617 11
        }
1618
1619
        $lines[] = $this->spaces . ' */';
1620
1621
        return implode("\n", $lines);
1622
    }
1623
1624
    /**
1625
     * @param array             $fieldMapping
1626 29
     * @param ClassMetadataInfo $metadata
1627
     *
1628 29
     * @return string
1629 29
     */
1630 29
    protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
1631 29
    {
1632 29
        $lines = [];
1633
        $lines[] = $this->spaces . '/**';
1634 29
        $lines[] = $this->spaces . ' * @var '
1635 29
            . $this->getType($fieldMapping['type'])
1636
            . ($this->nullableFieldExpression($fieldMapping) ? '|null' : '');
1637 29
1638 29
        if ($this->generateAnnotations) {
1639 29
            $lines[] = $this->spaces . ' *';
1640
1641
            $column = [];
1642 29
            if (isset($fieldMapping['columnName'])) {
1643 29
                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1644
            }
1645
1646 29
            if (isset($fieldMapping['type'])) {
1647 4
                $column[] = 'type="' . $fieldMapping['type'] . '"';
1648
            }
1649
1650 29
            if (isset($fieldMapping['length'])) {
1651 4
                $column[] = 'length=' . $fieldMapping['length'];
1652
            }
1653
1654 29
            if (isset($fieldMapping['precision'])) {
1655 4
                $column[] = 'precision=' .  $fieldMapping['precision'];
1656
            }
1657
1658 29
            if (isset($fieldMapping['scale'])) {
1659 10
                $column[] = 'scale=' . $fieldMapping['scale'];
1660
            }
1661
1662 29 View Code Duplication
            if (isset($fieldMapping['nullable'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1663
                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
1664 29
            }
1665 1
1666
            $options = [];
1667
1668 29
            if (isset($fieldMapping['options']['unsigned']) && $fieldMapping['options']['unsigned']) {
1669 1
                $options[] = '"unsigned"=true';
1670
            }
1671
1672 29
            if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1673 1
                $column[] = 'options={'.implode(',', $options).'}';
1674
            }
1675
1676 29
            if (isset($fieldMapping['columnDefinition'])) {
1677 4
                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1678
            }
1679
1680 29 View Code Duplication
            if (isset($fieldMapping['unique'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1681
                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1682 29
            }
1683 25
1684
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1685 25
1686 25
            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1687
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1688
1689 25 View Code Duplication
                if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1690 1
                    $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1691
                }
1692 1
1693 1
                if ($metadata->sequenceGeneratorDefinition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->sequenceGeneratorDefinition of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1694
                    $sequenceGenerator = [];
1695
1696 1
                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1697 1
                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1698
                    }
1699
1700 1
                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1701 1
                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1702
                    }
1703
1704 1
                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1705
                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1706
                    }
1707
1708 29
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1709
                }
1710
            }
1711
1712
            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1713 29
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
1714
            }
1715 29
        }
1716
1717
        $lines[] = $this->spaces . ' */';
1718
1719
        return implode("\n", $lines);
1720
    }
1721
1722
    /**
1723 10
     * @param array $embeddedClass
1724
     *
1725 10
     * @return string
1726 10
     */
1727 10
    protected function generateEmbeddedPropertyDocBlock(array $embeddedClass)
1728
    {
1729 10
        $lines = [];
1730 10
        $lines[] = $this->spaces . '/**';
1731
        $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\');
1732 10
1733
        if ($this->generateAnnotations) {
1734 10
            $lines[] = $this->spaces . ' *';
1735 8
1736 1
            $embedded = ['class="' . $embeddedClass['class'] . '"'];
1737
1738 7
            if (isset($embeddedClass['columnPrefix'])) {
1739
                if (is_string($embeddedClass['columnPrefix'])) {
1740
                    $embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"';
1741
                } else {
1742 10
                    $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
1743 10
                }
1744
            }
1745
1746 10
            $lines[] = $this->spaces . ' * @' .
1747
                $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')';
1748 10
        }
1749
1750
        $lines[] = $this->spaces . ' */';
1751
1752
        return implode("\n", $lines);
1753
    }
1754
1755
    private function generateEntityListenerAnnotation(ClassMetadataInfo $metadata): string
1756
    {
1757 31
        if (0 === \count($metadata->entityListeners)) {
1758
            return '';
1759 31
        }
1760
1761 31
        $processedClasses = [];
1762 31
        foreach ($metadata->entityListeners as $event => $eventListeners) {
1763 31
            foreach ($eventListeners as $eventListener) {
1764
                $processedClasses[] = '"' . $eventListener['class'] . '"';
1765
            }
1766
        }
1767 31
1768
        return \sprintf(
1769
            '%s%s({%s})',
1770
            '@' . $this->annotationsPrefix,
1771
            'EntityListeners',
1772
            \implode(',', \array_unique($processedClasses))
1773
        );
1774
    }
1775
1776
    /**
1777 1
     * @param string $code
1778
     * @param int    $num
1779 1
     *
1780 1
     * @return string
1781
     */
1782
    protected function prefixCodeWithSpaces($code, $num = 1)
1783 1
    {
1784
        $lines = explode("\n", $code);
1785
1786
        foreach ($lines as $key => $value) {
1787
            if ( ! empty($value)) {
1788
                $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1789
            }
1790
        }
1791
1792
        return implode("\n", $lines);
1793 1
    }
1794
1795 1
    /**
1796 1
     * @param integer $type The inheritance type used by the class and its subclasses.
1797
     *
1798
     * @return string The literal string for the inheritance type.
1799 1
     *
1800
     * @throws \InvalidArgumentException When the inheritance type does not exist.
1801
     */
1802
    protected function getInheritanceTypeString($type)
1803
    {
1804
        if ( ! isset(static::$inheritanceTypeMap[$type])) {
1805
            throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type));
1806
        }
1807
1808
        return static::$inheritanceTypeMap[$type];
1809 26
    }
1810
1811 26
    /**
1812 1
     * @param integer $type The policy used for change-tracking for the mapped class.
1813
     *
1814
     * @return string The literal string for the change-tracking type.
1815 26
     *
1816
     * @throws \InvalidArgumentException When the change-tracking type does not exist.
1817
     */
1818
    protected function getChangeTrackingPolicyString($type)
1819
    {
1820
        if ( ! isset(static::$changeTrackingPolicyMap[$type])) {
1821
            throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type));
1822
        }
1823 31
1824
        return static::$changeTrackingPolicyMap[$type];
1825 31
    }
1826 7
1827
    /**
1828
     * @param integer $type The generator to use for the mapped class.
1829 31
     *
1830
     * @return string The literal string for the generator type.
1831
     *
1832
     * @throws \InvalidArgumentException    When the generator type does not exist.
1833
     */
1834
    protected function getIdGeneratorTypeString($type)
1835
    {
1836
        if ( ! isset(static::$generatorStrategyMap[$type])) {
1837
            throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type));
1838
        }
1839 1
1840
        return static::$generatorStrategyMap[$type];
1841 1
    }
1842
1843 1
    /**
1844 1
     * @param array $fieldMapping
1845 1
     *
1846
     * @return string|null
1847 1
     */
1848
    private function nullableFieldExpression(array $fieldMapping)
1849
    {
1850
        if (isset($fieldMapping['nullable']) && true === $fieldMapping['nullable']) {
1851 1
            return 'null';
1852
        }
1853
1854
        return null;
1855
    }
1856
1857
    /**
1858
     * Exports (nested) option elements.
1859
     *
1860
     * @param array $options
1861
     *
1862
     * @return string
1863
     */
1864
    private function exportTableOptions(array $options)
1865
    {
1866
        $optionsStr = [];
1867
1868
        foreach ($options as $name => $option) {
1869
            if (is_array($option)) {
1870
                $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}';
1871
            } else {
1872
                $optionsStr[] = '"' . $name . '"="' . (string) $option . '"';
1873
            }
1874
        }
1875
1876
        return implode(',', $optionsStr);
1877
    }
1878
}
1879