Failed Conditions
Pull Request — master (#6593)
by Thomas
58:10 queued 48:01
created

EntityGenerator::generateEmbeddableConstructor()   D

Complexity

Conditions 11
Paths 120

Size

Total Lines 88
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 11.0008

Importance

Changes 0
Metric Value
dl 0
loc 88
ccs 51
cts 52
cp 0.9808
rs 4.9629
c 0
b 0
f 0
cc 11
eloc 53
nc 120
nop 1
crap 11.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 'namespace ' . $this->getNamespace($metadata) .';';
609
        }
610 2
    }
611
612 31
    protected function generateEntityUse()
613
    {
614 31
        if ($this->generateAnnotations) {
615 31
            return "\n".'use Doctrine\ORM\Mapping as ORM;'."\n";
616
        } else {
617
            return "";
618
        }
619
    }
620
621
    /**
622
     * @param ClassMetadataInfo $metadata
623
     *
624
     * @return string
625
     */
626 31
    protected function generateEntityClassName(ClassMetadataInfo $metadata)
627
    {
628 31
        return 'class ' . $this->getClassName($metadata) .
629 31
            ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
630
    }
631
632
    /**
633
     * @param ClassMetadataInfo $metadata
634
     *
635
     * @return string
636
     */
637 32
    protected function generateEntityBody(ClassMetadataInfo $metadata)
638
    {
639 32
        $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
640 32
        $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata);
641 32
        $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
642 32
        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
643 32
        $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
644
645 32
        $code = [];
646
647 32
        if ($fieldMappingProperties) {
648 29
            $code[] = $fieldMappingProperties;
649
        }
650
651 32
        if ($embeddedProperties) {
652 10
            $code[] = $embeddedProperties;
653
        }
654
655 32
        if ($associationMappingProperties) {
656 11
            $code[] = $associationMappingProperties;
657
        }
658
659 32
        $code[] = $this->generateEntityConstructor($metadata);
660
661 32
        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...
662 30
            $code[] = $stubMethods;
663
        }
664
665 32
        if ($lifecycleCallbackMethods) {
666 10
            $code[] = $lifecycleCallbackMethods;
667
        }
668
669 32
        return implode("\n", $code);
670
    }
671
672
    /**
673
     * @param ClassMetadataInfo $metadata
674
     *
675
     * @return string
676
     */
677 32
    protected function generateEntityConstructor(ClassMetadataInfo $metadata)
678
    {
679 32
        if ($this->hasMethod('__construct', $metadata)) {
680 2
            return '';
681
        }
682
683 32
        if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) {
684 1
            return $this->generateEmbeddableConstructor($metadata);
685
        }
686
687 31
        $collections = [];
688
689 31
        foreach ($metadata->associationMappings as $mapping) {
690 13
            if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
691 13
                $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
692
            }
693
        }
694
695 31
        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...
696 11
            return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate));
697
        }
698
699 27
        return '';
700
    }
701
702
    /**
703
     * @param ClassMetadataInfo $metadata
704
     *
705
     * @return string
706
     */
707 1
    private function generateEmbeddableConstructor(ClassMetadataInfo $metadata)
708
    {
709 1
        $paramTypes = [];
710 1
        $paramVariables = [];
711 1
        $params = [];
712 1
        $fields = [];
713
714
        // Resort fields to put optional fields at the end of the method signature.
715 1
        $requiredFields = [];
716 1
        $optionalFields = [];
717
718 1
        foreach ($metadata->fieldMappings as $fieldMapping) {
719 1
            if (empty($fieldMapping['nullable'])) {
720 1
                $requiredFields[] = $fieldMapping;
721
722 1
                continue;
723
            }
724
725 1
            $optionalFields[] = $fieldMapping;
726
        }
727
728 1
        $fieldMappings = array_merge($requiredFields, $optionalFields);
729
730 1
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
731 1
            $paramType = '\\' . ltrim($embeddedClass['class'], '\\');
732 1
            $paramVariable = '$' . $fieldName;
733
734 1
            $paramTypes[] = $paramType;
735 1
            $paramVariables[] = $paramVariable;
736 1
            $params[] = $paramType . ' ' . $paramVariable;
737 1
            $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';';
738
        }
739
740 1
        foreach ($fieldMappings as $fieldMapping) {
741 1
            if (isset($fieldMapping['declaredField']) &&
742 1
                isset($metadata->embeddedClasses[$fieldMapping['declaredField']])
743
            ) {
744
                continue;
745
            }
746
747 1
            $paramTypes[] = $this->getType($fieldMapping['type']) . (!empty($fieldMapping['nullable']) ? '|null' : '');
748 1
            $param = '$' . $fieldMapping['fieldName'];
749 1
            $paramVariables[] = $param;
750
751 1
            if ($fieldMapping['type'] === 'datetime') {
752 1
                $param = $this->getType($fieldMapping['type']) . ' ' . $param;
753
            }
754
755 1
            if (!empty($fieldMapping['nullable'])) {
756 1
                $param .= ' = null';
757
            }
758
759 1
            $params[] = $param;
760
761 1
            $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';';
762
        }
763
764 1
        $maxParamTypeLength = max(array_map('strlen', $paramTypes));
765 1
        $paramTags = array_map(
766 1
            function ($type, $variable) use ($maxParamTypeLength) {
767 1
                return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable;
768 1
            },
769 1
            $paramTypes,
770 1
            $paramVariables
771
        );
772
773
        // Generate multi line constructor if the signature exceeds 120 characters.
774 1
        if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) {
775 1
            $delimiter = "\n" . $this->spaces;
776 1
            $params = $delimiter . implode(',' . $delimiter, $params) . "\n";
777
        } else {
778 1
            $params = implode(', ', $params);
779
        }
780
781
        $replacements = [
782 1
            '<paramTags>' => implode("\n * ", $paramTags),
783 1
            '<params>'    => $params,
784 1
            '<fields>'    => implode("\n" . $this->spaces, $fields),
785
        ];
786
787 1
        $constructor = str_replace(
788 1
            array_keys($replacements),
789 1
            array_values($replacements),
790 1
            static::$embeddableConstructorMethodTemplate
791
        );
792
793 1
        return $this->prefixCodeWithSpaces($constructor);
794
    }
795
796
    /**
797
     * @todo this won't work if there is a namespace in brackets and a class outside of it.
798
     *
799
     * @param string $src
800
     *
801
     * @return void
802
     */
803 8
    protected function parseTokensInEntityFile($src)
804
    {
805 8
        $tokens = token_get_all($src);
806 8
        $tokensCount = count($tokens);
807 8
        $lastSeenNamespace = '';
808 8
        $lastSeenClass = false;
809
810 8
        $inNamespace = false;
811 8
        $inClass = false;
812
813 8
        for ($i = 0; $i < $tokensCount; $i++) {
814 8
            $token = $tokens[$i];
815 8
            if (in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
816 8
                continue;
817
            }
818
819 8
            if ($inNamespace) {
820 8
                if (in_array($token[0], [T_NS_SEPARATOR, T_STRING], true)) {
821 8
                    $lastSeenNamespace .= $token[1];
822 8
                } elseif (is_string($token) && in_array($token, [';', '{'], true)) {
823 8
                    $inNamespace = false;
824
                }
825
            }
826
827 8
            if ($inClass) {
828 8
                $inClass = false;
829 8
                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
830 8
                $this->staticReflection[$lastSeenClass]['properties'] = [];
831 8
                $this->staticReflection[$lastSeenClass]['methods'] = [];
832
            }
833
834 8
            if (T_NAMESPACE === $token[0]) {
835 8
                $lastSeenNamespace = '';
836 8
                $inNamespace = true;
837 8
            } elseif (T_CLASS === $token[0] && T_DOUBLE_COLON !== $tokens[$i-1][0]) {
838 8
                $inClass = true;
839 8
            } elseif (T_FUNCTION === $token[0]) {
840 3
                if (T_STRING === $tokens[$i+2][0]) {
841 3
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]);
842
                } elseif ($tokens[$i+2] == '&' && T_STRING === $tokens[$i+3][0]) {
843 3
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]);
844
                }
845 8
            } elseif (in_array($token[0], [T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED], true) && T_FUNCTION !== $tokens[$i+2][0]) {
846 4
                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
847
            }
848
        }
849 8
    }
850
851
    /**
852
     * @param string            $property
853
     * @param ClassMetadataInfo $metadata
854
     *
855
     * @return bool
856
     */
857 31 View Code Duplication
    protected function hasProperty($property, ClassMetadataInfo $metadata)
858
    {
859 31
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
860
            // don't generate property if its already on the base class.
861 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
862 2
            if ($reflClass->hasProperty($property)) {
863 1
                return true;
864
            }
865
        }
866
867
        // check traits for existing property
868 30
        foreach ($this->getTraits($metadata) as $trait) {
869 2
            if ($trait->hasProperty($property)) {
870 2
                return true;
871
            }
872
        }
873
874
        return (
875 30
            isset($this->staticReflection[$metadata->name]) &&
876 30
            in_array($property, $this->staticReflection[$metadata->name]['properties'], true)
877
        );
878
    }
879
880
    /**
881
     * @param string            $method
882
     * @param ClassMetadataInfo $metadata
883
     *
884
     * @return bool
885
     */
886 32 View Code Duplication
    protected function hasMethod($method, ClassMetadataInfo $metadata)
887
    {
888 32
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
889
            // don't generate method if its already on the base class.
890 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
891
892 2
            if ($reflClass->hasMethod($method)) {
893 1
                return true;
894
            }
895
        }
896
897
        // check traits for existing method
898 32
        foreach ($this->getTraits($metadata) as $trait) {
899 2
            if ($trait->hasMethod($method)) {
900 2
                return true;
901
            }
902
        }
903
904
        return (
905 32
            isset($this->staticReflection[$metadata->name]) &&
906 32
            in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods'], true)
907
        );
908
    }
909
910
    /**
911
     * @param ClassMetadataInfo $metadata
912
     *
913
     * @return array
914
     */
915 32
    protected function getTraits(ClassMetadataInfo $metadata)
916
    {
917 32
        if (! ($metadata->reflClass !== null || class_exists($metadata->name))) {
918 26
            return [];
919
        }
920
921 7
        $reflClass = $metadata->reflClass === null
922 1
            ? new \ReflectionClass($metadata->name)
923 7
            : $metadata->reflClass;
924
925 7
        $traits = [];
926
927 7
        while ($reflClass !== false) {
928 7
            $traits = array_merge($traits, $reflClass->getTraits());
929
930 7
            $reflClass = $reflClass->getParentClass();
931
        }
932
933 7
        return $traits;
934
    }
935
936
    /**
937
     * @param ClassMetadataInfo $metadata
938
     *
939
     * @return bool
940
     */
941 31
    protected function hasNamespace(ClassMetadataInfo $metadata)
942
    {
943 31
        return (bool) strpos($metadata->name, '\\');
944
    }
945
946
    /**
947
     * @return bool
948
     */
949 32
    protected function extendsClass()
950
    {
951 32
        return (bool) $this->classToExtend;
952
    }
953
954
    /**
955
     * @return string
956
     */
957 2
    protected function getClassToExtend()
958
    {
959 2
        return $this->classToExtend;
960
    }
961
962
    /**
963
     * @return string
964
     */
965 1
    protected function getClassToExtendName()
966
    {
967 1
        $refl = new \ReflectionClass($this->getClassToExtend());
968
969 1
        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...
970
    }
971
972
    /**
973
     * @param ClassMetadataInfo $metadata
974
     *
975
     * @return string
976
     */
977 32
    protected function getClassName(ClassMetadataInfo $metadata)
978
    {
979 32
        return ($pos = strrpos($metadata->name, '\\'))
980 32
            ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
981
    }
982
983
    /**
984
     * @param ClassMetadataInfo $metadata
985
     *
986
     * @return string
987
     */
988 31
    protected function getNamespace(ClassMetadataInfo $metadata)
989
    {
990 31
        return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
991
    }
992
993
    /**
994
     * @param ClassMetadataInfo $metadata
995
     *
996
     * @return string
997
     */
998 31
    protected function generateEntityDocBlock(ClassMetadataInfo $metadata)
999
    {
1000 31
        $lines = [];
1001 31
        $lines[] = '/**';
1002 31
        $lines[] = ' * ' . $this->getClassName($metadata);
1003
1004 31
        if ($this->generateAnnotations) {
1005 31
            $lines[] = ' *';
1006
1007
            $methods = [
1008 31
                'generateTableAnnotation',
1009
                'generateInheritanceAnnotation',
1010
                'generateDiscriminatorColumnAnnotation',
1011
                'generateDiscriminatorMapAnnotation',
1012
                'generateEntityAnnotation',
1013
                'generateEntityListenerAnnotation',
1014
            ];
1015
1016 31
            foreach ($methods as $method) {
1017 31
                if ($code = $this->$method($metadata)) {
1018 31
                    $lines[] = ' * ' . $code;
1019
                }
1020
            }
1021
1022 31
            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...
1023 10
                $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
1024
            }
1025
        }
1026
1027 31
        $lines[] = ' */';
1028
1029 31
        return implode("\n", $lines);
1030
    }
1031
1032
    /**
1033
     * @param ClassMetadataInfo $metadata
1034
     *
1035
     * @return string
1036
     */
1037 31
    protected function generateEntityAnnotation(ClassMetadataInfo $metadata)
1038
    {
1039 31
        $prefix = '@' . $this->annotationsPrefix;
1040
1041 31
        if ($metadata->isEmbeddedClass) {
1042 11
            return $prefix . 'Embeddable';
1043
        }
1044
1045 27
        $customRepository = $metadata->customRepositoryClassName
1046 11
            ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")'
1047 27
            : '';
1048
1049 27
        return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository;
1050
    }
1051
1052
    /**
1053
     * @param ClassMetadataInfo $metadata
1054
     *
1055
     * @return string
1056
     */
1057 31
    protected function generateTableAnnotation(ClassMetadataInfo $metadata)
1058
    {
1059 31
        if ($metadata->isEmbeddedClass) {
1060 11
            return '';
1061
        }
1062
1063 27
        $table = [];
1064
1065 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...
1066
            $table[] = 'schema="' . $metadata->table['schema'] . '"';
1067
        }
1068
1069 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...
1070 24
            $table[] = 'name="' . $metadata->table['name'] . '"';
1071
        }
1072
1073 27
        if (isset($metadata->table['options']) && $metadata->table['options']) {
1074 1
            $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}';
1075
        }
1076
1077 27 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...
1078 9
            $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
1079 9
            $table[] = 'uniqueConstraints={' . $constraints . '}';
1080
        }
1081
1082 27 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...
1083 9
            $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
1084 9
            $table[] = 'indexes={' . $constraints . '}';
1085
        }
1086
1087 27
        return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
1088
    }
1089
1090
    /**
1091
     * @param string $constraintName
1092
     * @param array  $constraints
1093
     *
1094
     * @return string
1095
     */
1096 9
    protected function generateTableConstraints($constraintName, array $constraints)
1097
    {
1098 9
        $annotations = [];
1099 9
        foreach ($constraints as $name => $constraint) {
1100 9
            $columns = [];
1101 9
            foreach ($constraint['columns'] as $column) {
1102 9
                $columns[] = '"' . $column . '"';
1103
            }
1104 9
            $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
1105
        }
1106
1107 9
        return implode(', ', $annotations);
1108
    }
1109
1110
    /**
1111
     * @param ClassMetadataInfo $metadata
1112
     *
1113
     * @return string
1114
     */
1115 31
    protected function generateInheritanceAnnotation(ClassMetadataInfo $metadata)
1116
    {
1117 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1118
            return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
1119
        }
1120 31
    }
1121
1122
    /**
1123
     * @param ClassMetadataInfo $metadata
1124
     *
1125
     * @return string
1126
     */
1127 31
    protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $metadata)
1128
    {
1129 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1130
            $discrColumn = $metadata->discriminatorColumn;
1131
            $columnDefinition = 'name="' . $discrColumn['name']
1132
                . '", type="' . $discrColumn['type']
1133
                . '", length=' . $discrColumn['length'];
1134
1135
            return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
1136
        }
1137 31
    }
1138
1139
    /**
1140
     * @param ClassMetadataInfo $metadata
1141
     *
1142
     * @return string
1143
     */
1144 31
    protected function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata)
1145
    {
1146 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1147
            $inheritanceClassMap = [];
1148
1149
            foreach ($metadata->discriminatorMap as $type => $class) {
1150
                $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
1151
            }
1152
1153
            return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
1154
        }
1155 31
    }
1156
1157
    /**
1158
     * @param ClassMetadataInfo $metadata
1159
     *
1160
     * @return string
1161
     */
1162 31
    protected function generateEntityStubMethods(ClassMetadataInfo $metadata)
1163
    {
1164 31
        $methods = [];
1165
1166 31
        foreach ($metadata->fieldMappings as $fieldMapping) {
1167 30
            if (isset($fieldMapping['declaredField']) &&
1168 30
                isset($metadata->embeddedClasses[$fieldMapping['declaredField']])
1169
            ) {
1170
                continue;
1171
            }
1172
1173 30
            $nullableField = $this->nullableFieldExpression($fieldMapping);
1174
1175 30
            if (( ! isset($fieldMapping['id']) ||
1176 26
                    ! $fieldMapping['id'] ||
1177 30
                    $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE
1178 30
                ) && (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable)
1179 30
                && $code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)
1180
            ) {
1181 27
                $methods[] = $code;
1182
            }
1183
1184 30
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)) {
1185 30
                $methods[] = $code;
1186
            }
1187
        }
1188
1189 31
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1190 10
            if (isset($embeddedClass['declaredField'])) {
1191 1
                continue;
1192
            }
1193
1194 10
            if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) {
1195 9
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) {
1196 9
                    $methods[] = $code;
1197
                }
1198
            }
1199
1200 10
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) {
1201 10
                $methods[] = $code;
1202
            }
1203
        }
1204
1205 31
        foreach ($metadata->associationMappings as $associationMapping) {
1206 12
            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
1207 11
                $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
1208 11
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1209 9
                    $methods[] = $code;
1210
                }
1211 11
                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1212 11
                    $methods[] = $code;
1213
                }
1214 10
            } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1215 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...
1216 10
                    $methods[] = $code;
1217
                }
1218 10 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...
1219 10
                    $methods[] = $code;
1220
                }
1221 10 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...
1222 12
                    $methods[] = $code;
1223
                }
1224
            }
1225
        }
1226
1227 31
        return implode("\n\n", $methods);
1228
    }
1229
1230
    /**
1231
     * @param array $associationMapping
1232
     *
1233
     * @return bool
1234
     */
1235 11
    protected function isAssociationIsNullable(array $associationMapping)
1236
    {
1237 11
        if (isset($associationMapping['id']) && $associationMapping['id']) {
1238
            return false;
1239
        }
1240
1241 11
        if (isset($associationMapping['joinColumns'])) {
1242 2
            $joinColumns = $associationMapping['joinColumns'];
1243
        } else {
1244
            //@todo there is no way to retrieve targetEntity metadata
1245 9
            $joinColumns = [];
1246
        }
1247
1248 11
        foreach ($joinColumns as $joinColumn) {
1249 2
            if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
1250 2
                return false;
1251
            }
1252
        }
1253
1254 11
        return true;
1255
    }
1256
1257
    /**
1258
     * @param ClassMetadataInfo $metadata
1259
     *
1260
     * @return string
1261
     */
1262 32
    protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
1263
    {
1264 32
        if (empty($metadata->lifecycleCallbacks)) {
1265 29
            return '';
1266
        }
1267
1268 10
        $methods = [];
1269
1270 10
        foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
1271 10
            foreach ($callbacks as $callback) {
1272 10
                $methods[] = $this->generateLifecycleCallbackMethod($name, $callback, $metadata);
1273
            }
1274
        }
1275
1276 10
        return implode("\n\n", array_filter($methods));
1277
    }
1278
1279
    /**
1280
     * @param ClassMetadataInfo $metadata
1281
     *
1282
     * @return string
1283
     */
1284 32
    protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
1285
    {
1286 32
        $lines = [];
1287
1288 32
        foreach ($metadata->associationMappings as $associationMapping) {
1289 13
            if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
1290 4
                continue;
1291
            }
1292
1293 11
            $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
1294 11
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
1295 11
                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
1296
        }
1297
1298 32
        return implode("\n", $lines);
1299
    }
1300
1301
    /**
1302
     * @param ClassMetadataInfo $metadata
1303
     *
1304
     * @return string
1305
     */
1306 32
    protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
1307
    {
1308 32
        $lines = [];
1309
1310 32
        foreach ($metadata->fieldMappings as $fieldMapping) {
1311 31
            if ($this->hasProperty($fieldMapping['fieldName'], $metadata) ||
1312 30
                $metadata->isInheritedField($fieldMapping['fieldName']) ||
1313
                (
1314 29
                    isset($fieldMapping['declaredField']) &&
1315 31
                    isset($metadata->embeddedClasses[$fieldMapping['declaredField']])
1316
                )
1317
            ) {
1318 4
                continue;
1319
            }
1320
1321 29
            $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
1322 29
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
1323 29
                     . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n";
1324
        }
1325
1326 32
        return implode("\n", $lines);
1327
    }
1328
1329
    /**
1330
     * @param ClassMetadataInfo $metadata
1331
     *
1332
     * @return string
1333
     */
1334 32
    protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata)
1335
    {
1336 32
        $lines = [];
1337
1338 32
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1339 10
            if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) {
1340 2
                continue;
1341
            }
1342
1343 10
            $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass);
1344 10
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n";
1345
        }
1346
1347 32
        return implode("\n", $lines);
1348
    }
1349
1350
    /**
1351
     * @param ClassMetadataInfo $metadata
1352
     * @param string            $type
1353
     * @param string            $fieldName
1354
     * @param string|null       $typeHint
1355
     * @param string|null       $defaultValue
1356
     *
1357
     * @return string
1358
     */
1359 30
    protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
1360
    {
1361 30
        $methodName = $type . Inflector::classify($fieldName);
1362 30
        $variableName = Inflector::camelize($fieldName);
1363 30
        if (in_array($type, ["add", "remove"])) {
1364 10
            $methodName = Inflector::singularize($methodName);
1365 10
            $variableName = Inflector::singularize($variableName);
1366
        }
1367
1368 30
        if ($this->hasMethod($methodName, $metadata)) {
1369 5
            return '';
1370
        }
1371 30
        $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName);
1372
1373 30
        $var = sprintf('%sMethodTemplate', $type);
1374 30
        $template = static::$$var;
1375
1376 30
        $methodTypeHint = null;
1377 30
        $types          = Type::getTypesMap();
1378 30
        $variableType   = $typeHint ? $this->getType($typeHint) : null;
1379
1380 30
        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...
1381 14
            $variableType   =  '\\' . ltrim($variableType, '\\');
1382 14
            $methodTypeHint =  '\\' . $typeHint . ' ';
1383
        }
1384
1385
        $replacements = [
1386 30
          '<description>'       => ucfirst($type) . ' ' . $variableName . '.',
1387 30
          '<methodTypeHint>'    => $methodTypeHint,
1388 30
          '<variableType>'      => $variableType . (null !== $defaultValue ? ('|' . $defaultValue) : ''),
1389 30
          '<variableName>'      => $variableName,
1390 30
          '<methodName>'        => $methodName,
1391 30
          '<fieldName>'         => $fieldName,
1392 30
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = ' . $defaultValue) : '',
1393 30
          '<entity>'            => $this->getClassName($metadata)
1394
        ];
1395
1396 30
        $method = str_replace(
1397 30
            array_keys($replacements),
1398 30
            array_values($replacements),
1399 30
            $template
1400
        );
1401
1402 30
        return $this->prefixCodeWithSpaces($method);
1403
    }
1404
1405
    /**
1406
     * @param string            $name
1407
     * @param string            $methodName
1408
     * @param ClassMetadataInfo $metadata
1409
     *
1410
     * @return string
1411
     */
1412 10
    protected function generateLifecycleCallbackMethod($name, $methodName, ClassMetadataInfo $metadata)
1413
    {
1414 10
        if ($this->hasMethod($methodName, $metadata)) {
1415 2
            return '';
1416
        }
1417 10
        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
1418
1419
        $replacements = [
1420 10
            '<name>'        => $this->annotationsPrefix . ucfirst($name),
1421 10
            '<methodName>'  => $methodName,
1422
        ];
1423
1424 10
        $method = str_replace(
1425 10
            array_keys($replacements),
1426 10
            array_values($replacements),
1427 10
            static::$lifecycleCallbackMethodTemplate
1428
        );
1429
1430 10
        return $this->prefixCodeWithSpaces($method);
1431
    }
1432
1433
    /**
1434
     * @param array $joinColumn
1435
     *
1436
     * @return string
1437
     */
1438 11
    protected function generateJoinColumnAnnotation(array $joinColumn)
1439
    {
1440 11
        $joinColumnAnnot = [];
1441
1442 11
        if (isset($joinColumn['name'])) {
1443 11
            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
1444
        }
1445
1446 11
        if (isset($joinColumn['referencedColumnName'])) {
1447 11
            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
1448
        }
1449
1450 11
        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
1451 1
            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
1452
        }
1453
1454 11
        if (isset($joinColumn['nullable'])) {
1455 1
            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
1456
        }
1457
1458 11
        if (isset($joinColumn['onDelete'])) {
1459 1
            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
1460
        }
1461
1462 11
        if (isset($joinColumn['columnDefinition'])) {
1463 1
            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
1464
        }
1465
1466 11
        return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
1467
    }
1468
1469
    /**
1470
     * @param array             $associationMapping
1471
     * @param ClassMetadataInfo $metadata
1472
     *
1473
     * @return string
1474
     */
1475 11
    protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
1476
    {
1477 11
        $lines = [];
1478 11
        $lines[] = $this->spaces . '/**';
1479
1480 11
        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1481 11
            $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection';
1482
        } else {
1483 10
            $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\');
1484
        }
1485
1486 11
        if ($this->generateAnnotations) {
1487 11
            $lines[] = $this->spaces . ' *';
1488
1489 11
            if (isset($associationMapping['id']) && $associationMapping['id']) {
1490
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1491
1492 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...
1493
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1494
                }
1495
            }
1496
1497 11
            $type = null;
1498 11
            switch ($associationMapping['type']) {
1499 11
                case ClassMetadataInfo::ONE_TO_ONE:
1500 10
                    $type = 'OneToOne';
1501 10
                    break;
1502 11
                case ClassMetadataInfo::MANY_TO_ONE:
1503 1
                    $type = 'ManyToOne';
1504 1
                    break;
1505 11
                case ClassMetadataInfo::ONE_TO_MANY:
1506 1
                    $type = 'OneToMany';
1507 1
                    break;
1508 11
                case ClassMetadataInfo::MANY_TO_MANY:
1509 11
                    $type = 'ManyToMany';
1510 11
                    break;
1511
            }
1512 11
            $typeOptions = [];
1513
1514 11
            if (isset($associationMapping['targetEntity'])) {
1515 11
                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
1516
            }
1517
1518 11
            if (isset($associationMapping['inversedBy'])) {
1519 1
                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
1520
            }
1521
1522 11
            if (isset($associationMapping['mappedBy'])) {
1523 10
                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
1524
            }
1525
1526 11
            if ($associationMapping['cascade']) {
1527 1
                $cascades = [];
1528
1529 1
                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
1530 1
                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
1531 1
                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
1532 1
                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
1533 1
                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
1534
1535 1
                if (count($cascades) === 5) {
1536 1
                    $cascades = ['"all"'];
1537
                }
1538
1539 1
                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
1540
            }
1541
1542 11
            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
1543 1
                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
1544
            }
1545
1546 11
            if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) {
1547
                $fetchMap = [
1548 10
                    ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY',
1549
                    ClassMetadataInfo::FETCH_EAGER      => 'EAGER',
1550
                ];
1551
1552 10
                $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"';
1553
            }
1554
1555 11
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
1556
1557 11
            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
1558 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
1559
1560 1
                $joinColumnsLines = [];
1561
1562 1 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...
1563 1
                    if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
1564 1
                        $joinColumnsLines[] = $this->spaces . ' *   ' . $joinColumnAnnot;
1565
                    }
1566
                }
1567
1568 1
                $lines[] = implode(",\n", $joinColumnsLines);
1569 1
                $lines[] = $this->spaces . ' * })';
1570
            }
1571
1572 11
            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
1573 11
                $joinTable = [];
1574 11
                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
1575
1576 11
                if (isset($associationMapping['joinTable']['schema'])) {
1577
                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
1578
                }
1579
1580 11
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
1581 11
                $lines[] = $this->spaces . ' *   joinColumns={';
1582
1583 11
                $joinColumnsLines = [];
1584
1585 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...
1586 11
                    $joinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1587
                }
1588
1589 11
                $lines[] = implode(",". PHP_EOL, $joinColumnsLines);
1590 11
                $lines[] = $this->spaces . ' *   },';
1591 11
                $lines[] = $this->spaces . ' *   inverseJoinColumns={';
1592
1593 11
                $inverseJoinColumnsLines = [];
1594
1595 11
                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
1596 11
                    $inverseJoinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1597
                }
1598
1599 11
                $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines);
1600 11
                $lines[] = $this->spaces . ' *   }';
1601 11
                $lines[] = $this->spaces . ' * )';
1602
            }
1603
1604 11
            if (isset($associationMapping['orderBy'])) {
1605 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
1606
1607 1
                foreach ($associationMapping['orderBy'] as $name => $direction) {
1608 1
                    $lines[] = $this->spaces . ' *     "' . $name . '"="' . $direction . '",';
1609
                }
1610
1611 1
                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
1612 1
                $lines[] = $this->spaces . ' * })';
1613
            }
1614
        }
1615
1616 11
        $lines[] = $this->spaces . ' */';
1617
1618 11
        return implode("\n", $lines);
1619
    }
1620
1621
    /**
1622
     * @param array             $fieldMapping
1623
     * @param ClassMetadataInfo $metadata
1624
     *
1625
     * @return string
1626
     */
1627 29
    protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
1628
    {
1629 29
        $lines = [];
1630 29
        $lines[] = $this->spaces . '/**';
1631 29
        $lines[] = $this->spaces . ' * @var '
1632 29
            . $this->getType($fieldMapping['type'])
1633 29
            . ($this->nullableFieldExpression($fieldMapping) ? '|null' : '');
1634
1635 29
        if ($this->generateAnnotations) {
1636 29
            $lines[] = $this->spaces . ' *';
1637
1638 29
            $column = [];
1639 29
            if (isset($fieldMapping['columnName'])) {
1640 29
                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1641
            }
1642
1643 29
            if (isset($fieldMapping['type'])) {
1644 29
                $column[] = 'type="' . $fieldMapping['type'] . '"';
1645
            }
1646
1647 29
            if (isset($fieldMapping['length'])) {
1648 4
                $column[] = 'length=' . $fieldMapping['length'];
1649
            }
1650
1651 29
            if (isset($fieldMapping['precision'])) {
1652 4
                $column[] = 'precision=' .  $fieldMapping['precision'];
1653
            }
1654
1655 29
            if (isset($fieldMapping['scale'])) {
1656 4
                $column[] = 'scale=' . $fieldMapping['scale'];
1657
            }
1658
1659 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...
1660 10
                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
1661
            }
1662
1663 29
            $options = [];
1664
1665 29
            if (isset($fieldMapping['options']['unsigned']) && $fieldMapping['options']['unsigned']) {
1666 1
                $options[] = '"unsigned"=true';
1667
            }
1668
1669 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...
1670 1
                $column[] = 'options={'.implode(',', $options).'}';
1671
            }
1672
1673 29
            if (isset($fieldMapping['columnDefinition'])) {
1674 1
                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1675
            }
1676
1677 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...
1678 4
                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1679
            }
1680
1681 29
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1682
1683 29
            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1684 25
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1685
1686 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...
1687 25
                    $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1688
                }
1689
1690 25
                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...
1691 1
                    $sequenceGenerator = [];
1692
1693 1
                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1694 1
                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1695
                    }
1696
1697 1
                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1698 1
                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1699
                    }
1700
1701 1
                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1702 1
                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1703
                    }
1704
1705 1
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1706
                }
1707
            }
1708
1709 29
            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1710
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
1711
            }
1712
        }
1713
1714 29
        $lines[] = $this->spaces . ' */';
1715
1716 29
        return implode("\n", $lines);
1717
    }
1718
1719
    /**
1720
     * @param array $embeddedClass
1721
     *
1722
     * @return string
1723
     */
1724 10
    protected function generateEmbeddedPropertyDocBlock(array $embeddedClass)
1725
    {
1726 10
        $lines = [];
1727 10
        $lines[] = $this->spaces . '/**';
1728 10
        $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\');
1729
1730 10
        if ($this->generateAnnotations) {
1731 10
            $lines[] = $this->spaces . ' *';
1732
1733 10
            $embedded = ['class="' . $embeddedClass['class'] . '"'];
1734
1735 10
            if (isset($embeddedClass['columnPrefix'])) {
1736 8
                if (is_string($embeddedClass['columnPrefix'])) {
1737 1
                    $embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"';
1738
                } else {
1739 7
                    $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
1740
                }
1741
            }
1742
1743 10
            $lines[] = $this->spaces . ' * @' .
1744 10
                $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')';
1745
        }
1746
1747 10
        $lines[] = $this->spaces . ' */';
1748
1749 10
        return implode("\n", $lines);
1750
    }
1751
1752
    /**
1753
     * @param ClassMetadataInfo $metadata
1754
     *
1755
     * @return string
1756
     */
1757 31
    protected function generateEntityListenerAnnotation(ClassMetadataInfo $metadata)
1758
    {
1759 31
        if (0 === \count($metadata->entityListeners)) {
1760 30
            return '';
1761
        }
1762
1763 1
        $processedClasses = [];
1764 1
        foreach ($metadata->entityListeners as $event => $eventListeners) {
1765 1
            foreach ($eventListeners as $eventListener) {
1766 1
                $processedClasses[] = '"' . $eventListener['class'] . '"';
1767
            }
1768
        }
1769
1770 1
        return \sprintf(
1771 1
            '%s%s({%s})',
1772 1
            '@' . $this->annotationsPrefix,
1773 1
            'EntityListeners',
1774 1
            \implode(',', \array_unique($processedClasses))
1775
        );
1776
    }
1777
1778
    /**
1779
     * @param string $code
1780
     * @param int    $num
1781
     *
1782
     * @return string
1783
     */
1784 31
    protected function prefixCodeWithSpaces($code, $num = 1)
1785
    {
1786 31
        $lines = explode("\n", $code);
1787
1788 31
        foreach ($lines as $key => $value) {
1789 31
            if ( ! empty($value)) {
1790 31
                $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1791
            }
1792
        }
1793
1794 31
        return implode("\n", $lines);
1795
    }
1796
1797
    /**
1798
     * @param integer $type The inheritance type used by the class and its subclasses.
1799
     *
1800
     * @return string The literal string for the inheritance type.
1801
     *
1802
     * @throws \InvalidArgumentException When the inheritance type does not exist.
1803
     */
1804 1
    protected function getInheritanceTypeString($type)
1805
    {
1806 1
        if ( ! isset(static::$inheritanceTypeMap[$type])) {
1807 1
            throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type));
1808
        }
1809
1810 1
        return static::$inheritanceTypeMap[$type];
1811
    }
1812
1813
    /**
1814
     * @param integer $type The policy used for change-tracking for the mapped class.
1815
     *
1816
     * @return string The literal string for the change-tracking type.
1817
     *
1818
     * @throws \InvalidArgumentException When the change-tracking type does not exist.
1819
     */
1820 1
    protected function getChangeTrackingPolicyString($type)
1821
    {
1822 1
        if ( ! isset(static::$changeTrackingPolicyMap[$type])) {
1823 1
            throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type));
1824
        }
1825
1826 1
        return static::$changeTrackingPolicyMap[$type];
1827
    }
1828
1829
    /**
1830
     * @param integer $type The generator to use for the mapped class.
1831
     *
1832
     * @return string The literal string for the generator type.
1833
     *
1834
     * @throws \InvalidArgumentException    When the generator type does not exist.
1835
     */
1836 26
    protected function getIdGeneratorTypeString($type)
1837
    {
1838 26
        if ( ! isset(static::$generatorStrategyMap[$type])) {
1839 1
            throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type));
1840
        }
1841
1842 26
        return static::$generatorStrategyMap[$type];
1843
    }
1844
1845
    /**
1846
     * @param array $fieldMapping
1847
     *
1848
     * @return string|null
1849
     */
1850 31
    private function nullableFieldExpression(array $fieldMapping)
1851
    {
1852 31
        if (isset($fieldMapping['nullable']) && true === $fieldMapping['nullable']) {
1853 7
            return 'null';
1854
        }
1855
1856 31
        return null;
1857
    }
1858
1859
    /**
1860
     * Exports (nested) option elements.
1861
     *
1862
     * @param array $options
1863
     *
1864
     * @return string
1865
     */
1866 1
    private function exportTableOptions(array $options)
1867
    {
1868 1
        $optionsStr = [];
1869
1870 1
        foreach ($options as $name => $option) {
1871 1
            if (is_array($option)) {
1872 1
                $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}';
1873
            } else {
1874 1
                $optionsStr[] = '"' . $name . '"="' . (string) $option . '"';
1875
            }
1876
        }
1877
1878 1
        return implode(',', $optionsStr);
1879
    }
1880
}
1881