Failed Conditions
Pull Request — master (#6832)
by Jérémy
19:32
created

EntityGenerator::setGenerateAnnotations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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
     * Whether or not to add PHP strict types.
152
     *
153
     * @var boolean
154
     */
155
    protected $strictTypes = false;
156
157
    /**
158
     * Whether or not to force type hinting for method result and scalar parameters.
159
     *
160
     * @var boolean
161
     */
162
    protected $generateMethodsTypeHinting = false;
163
164
    /**
165
     * Hash-map for handle types.
166
     *
167
     * @var array
168
     */
169
    protected $typeAlias = [
170
        Type::DATETIMETZ    => '\DateTime',
171
        Type::DATETIME      => '\DateTime',
172
        Type::DATE          => '\DateTime',
173
        Type::TIME          => '\DateTime',
174
        Type::OBJECT        => '\stdClass',
175
        Type::INTEGER       => 'int',
176
        Type::BIGINT        => 'int',
177
        Type::SMALLINT      => 'int',
178
        Type::TEXT          => 'string',
179
        Type::BLOB          => 'string',
180
        Type::DECIMAL       => 'string',
181
        Type::JSON_ARRAY    => 'array',
182
        Type::SIMPLE_ARRAY  => 'array',
183
        Type::BOOLEAN       => 'bool',
184
    ];
185
186
    /**
187
     * Hash-map for handle strict types
188
     *
189
     * @var array
190
     */
191
    protected $typeHintingAlias = array(
192
        Type::TARRAY        => 'array',
193
        Type::SIMPLE_ARRAY  => 'array',
194
        Type::JSON_ARRAY    => 'array',
195
        Type::STRING        => 'string',
196
        Type::FLOAT         => 'float',
197
        Type::GUID          => 'string',
198
    );
199
200
    /**
201
     * Hash-map to handle generator types string.
202
     *
203
     * @var array
204
     */
205
    protected static $generatorStrategyMap = [
206
        ClassMetadataInfo::GENERATOR_TYPE_AUTO      => 'AUTO',
207
        ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE  => 'SEQUENCE',
208
        ClassMetadataInfo::GENERATOR_TYPE_TABLE     => 'TABLE',
209
        ClassMetadataInfo::GENERATOR_TYPE_IDENTITY  => 'IDENTITY',
210
        ClassMetadataInfo::GENERATOR_TYPE_NONE      => 'NONE',
211
        ClassMetadataInfo::GENERATOR_TYPE_UUID      => 'UUID',
212
        ClassMetadataInfo::GENERATOR_TYPE_CUSTOM    => 'CUSTOM'
213
    ];
214
215
    /**
216
     * Hash-map to handle the change tracking policy string.
217
     *
218
     * @var array
219
     */
220
    protected static $changeTrackingPolicyMap = [
221
        ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT  => 'DEFERRED_IMPLICIT',
222
        ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT  => 'DEFERRED_EXPLICIT',
223
        ClassMetadataInfo::CHANGETRACKING_NOTIFY             => 'NOTIFY',
224
    ];
225
226
    /**
227
     * Hash-map to handle the inheritance type string.
228
     *
229
     * @var array
230
     */
231
    protected static $inheritanceTypeMap = [
232
        ClassMetadataInfo::INHERITANCE_TYPE_NONE            => 'NONE',
233
        ClassMetadataInfo::INHERITANCE_TYPE_JOINED          => 'JOINED',
234
        ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE    => 'SINGLE_TABLE',
235
        ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS',
236
    ];
237
238
    /**
239
     * @var string
240
     */
241
    protected static $classTemplate =
242
'<?php
243
<strictTypes>
244
<namespace>
245
<useStatement>
246
<entityAnnotation>
247
<entityClassName>
248
{
249
<entityBody>
250
}
251
';
252
253
    /**
254
     * @var string
255
     */
256
    protected static $getMethodTemplate =
257
'/**
258
 * <description>
259
 *
260
 * @return <variableType>
261
 */
262
public function <methodName>() <methodTypeReturn>
263
{
264
<spaces>return $this-><fieldName>;
265
}';
266
267
    /**
268
     * @var string
269
     */
270
    protected static $setMethodTemplate =
271
'/**
272
 * <description>
273
 *
274
 * @param <variableType> $<variableName>
275
 *
276
 * @return <entity>
277
 */
278
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>) <methodTypeReturn>
279
{
280
<spaces>$this-><fieldName> = $<variableName>;
281
282
<spaces>return $this;
283
}';
284
285
    /**
286
     * @var string
287
     */
288
    protected static $addMethodTemplate =
289
'/**
290
 * <description>
291
 *
292
 * @param <variableType> $<variableName>
293
 *
294
 * @return <entity>
295
 */
296
public function <methodName>(<methodTypeHint>$<variableName>) <methodTypeReturn>
297
{
298
<spaces>$this-><fieldName>[] = $<variableName>;
299
300
<spaces>return $this;
301
}';
302
303
    /**
304
     * @var string
305
     */
306
    protected static $removeMethodTemplate =
307
'/**
308
 * <description>
309
 *
310
 * @param <variableType> $<variableName>
311
 *
312
 * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
313
 */
314
public function <methodName>(<methodTypeHint>$<variableName>) <methodTypeReturn>
315
{
316
<spaces>return $this-><fieldName>->removeElement($<variableName>);
317
}';
318
319
    /**
320
     * @var string
321
     */
322
    protected static $lifecycleCallbackMethodTemplate =
323
'/**
324
 * @<name>
325
 */
326
public function <methodName>()
327
{
328
<spaces>// Add your code here
329
}';
330
331
    /**
332
     * @var string
333
     */
334
    protected static $constructorMethodTemplate =
335
'/**
336
 * Constructor
337
 */
338
public function __construct()
339
{
340
<spaces><collections>
341
}
342
';
343
344
    /**
345
     * @var string
346
     */
347
    protected static $embeddableConstructorMethodTemplate =
348
'/**
349
 * Constructor
350
 *
351
 * <paramTags>
352
 */
353
public function __construct(<params>)
354
{
355
<spaces><fields>
356
}
357
';
358
359
    /**
360
     * Constructor.
361
     */
362 51
    public function __construct()
363
    {
364 51
        if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
365 51
            $this->annotationsPrefix = 'ORM\\';
366
        }
367 51
    }
368
369
    /**
370
     * Generates and writes entity classes for the given array of ClassMetadataInfo instances.
371
     *
372
     * @param array  $metadatas
373
     * @param string $outputDirectory
374
     *
375
     * @return void
376
     */
377
    public function generate(array $metadatas, $outputDirectory)
378
    {
379
        foreach ($metadatas as $metadata) {
380
            $this->writeEntityClass($metadata, $outputDirectory);
381
        }
382
    }
383
384
    /**
385
     * Generates and writes entity class to disk for the given ClassMetadataInfo instance.
386
     *
387
     * @param ClassMetadataInfo $metadata
388
     * @param string            $outputDirectory
389
     *
390
     * @return void
391
     *
392
     * @throws \RuntimeException
393
     */
394 42
    public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
395
    {
396 42
        $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension;
397 42
        $dir = dirname($path);
398
399 42
        if ( ! is_dir($dir)) {
400 2
            mkdir($dir, 0775, true);
401
        }
402
403 42
        $this->isNew = ! file_exists($path) || $this->regenerateEntityIfExists;
404
405 42
        if ( ! $this->isNew) {
406 3
            $this->parseTokensInEntityFile(file_get_contents($path));
407
        } else {
408 41
            $this->staticReflection[$metadata->name] = ['properties' => [], 'methods' => []];
409
        }
410
411 42
        if ($this->backupExisting && file_exists($path)) {
412 3
            $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
413 3
            if (!copy($path, $backupPath)) {
414
                throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
415
            }
416
        }
417
418
        // If entity doesn't exist or we're re-generating the entities entirely
419 42
        if ($this->isNew) {
420 41
            file_put_contents($path, $this->generateEntityClass($metadata));
421
        // If entity exists and we're allowed to update the entity class
422 3
        } elseif ($this->updateEntityIfExists) {
423 3
            file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
424
        }
425 42
        chmod($path, 0664);
426 42
    }
427
428
    /**
429
     * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance.
430
     *
431
     * @param ClassMetadataInfo $metadata
432
     *
433
     * @return string
434
     */
435 42
    public function generateEntityClass(ClassMetadataInfo $metadata)
436
    {
437
        $placeHolders = [
438 42
            '<strictTypes>',
439
            '<namespace>',
440
            '<useStatement>',
441
            '<entityAnnotation>',
442
            '<entityClassName>',
443
            '<entityBody>'
444
        ];
445
446
        $replacements = [
447 42
            $this->generateEntityStrictTypes(),
448 42
            $this->generateEntityNamespace($metadata),
449 42
            $this->generateEntityUse(),
450 42
            $this->generateEntityDocBlock($metadata),
451 42
            $this->generateEntityClassName($metadata),
452 42
            $this->generateEntityBody($metadata)
453
        ];
454
455 42
        $code = str_replace($placeHolders, $replacements, static::$classTemplate);
456
457 42
        return str_replace('<spaces>', $this->spaces, $code);
458
    }
459
460
    /**
461
     * Generates the updated code for the given ClassMetadataInfo and entity at path.
462
     *
463
     * @param ClassMetadataInfo $metadata
464
     * @param string            $path
465
     *
466
     * @return string
467
     */
468 3
    public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
469
    {
470 3
        $currentCode = file_get_contents($path);
471
472 3
        $body = $this->generateEntityBody($metadata);
473 3
        $body = str_replace('<spaces>', $this->spaces, $body);
474 3
        $last = strrpos($currentCode, '}');
475
476 3
        return substr($currentCode, 0, $last) . $body . ($body ? "\n" : '') . "}\n";
477
    }
478
479
    /**
480
     * Sets the number of spaces the exported class should have.
481
     *
482
     * @param integer $numSpaces
483
     *
484
     * @return void
485
     */
486
    public function setNumSpaces($numSpaces)
487
    {
488
        $this->spaces = str_repeat(' ', $numSpaces);
489
        $this->numSpaces = $numSpaces;
490
    }
491
492
    /**
493
     * Sets the extension to use when writing php files to disk.
494
     *
495
     * @param string $extension
496
     *
497
     * @return void
498
     */
499
    public function setExtension($extension)
500
    {
501
        $this->extension = $extension;
502
    }
503
504
    /**
505
     * Sets the name of the class the generated classes should extend from.
506
     *
507
     * @param string $classToExtend
508
     *
509
     * @return void
510
     */
511 1
    public function setClassToExtend($classToExtend)
512
    {
513 1
        $this->classToExtend = $classToExtend;
514 1
    }
515
516
    /**
517
     * Sets whether or not to generate annotations for the entity.
518
     *
519
     * @param bool $bool
520
     *
521
     * @return void
522
     */
523 51
    public function setGenerateAnnotations($bool)
524
    {
525 51
        $this->generateAnnotations = $bool;
526 51
    }
527
528
    /**
529
     * Sets the class fields visibility for the entity (can either be private or protected).
530
     *
531
     * @param bool $visibility
532
     *
533
     * @return void
534
     *
535
     * @throws \InvalidArgumentException
536
     */
537 50
    public function setFieldVisibility($visibility)
538
    {
539 50
        if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) {
540
            throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility);
541
        }
542
543 50
        $this->fieldVisibility = $visibility;
544 50
    }
545
546
    /**
547
     * Sets whether or not to generate immutable embeddables.
548
     *
549
     * @param boolean $embeddablesImmutable
550
     */
551 1
    public function setEmbeddablesImmutable($embeddablesImmutable)
552
    {
553 1
        $this->embeddablesImmutable = (boolean) $embeddablesImmutable;
554 1
    }
555
556
    /**
557
     * Sets an annotation prefix.
558
     *
559
     * @param string $prefix
560
     *
561
     * @return void
562
     */
563 51
    public function setAnnotationPrefix($prefix)
564
    {
565 51
        $this->annotationsPrefix = $prefix;
566 51
    }
567
568
    /**
569
     * Sets whether or not to try and update the entity if it already exists.
570
     *
571
     * @param bool $bool
572
     *
573
     * @return void
574
     */
575 51
    public function setUpdateEntityIfExists($bool)
576
    {
577 51
        $this->updateEntityIfExists = $bool;
578 51
    }
579
580
    /**
581
     * Sets whether or not to regenerate the entity if it exists.
582
     *
583
     * @param bool $bool
584
     *
585
     * @return void
586
     */
587 51
    public function setRegenerateEntityIfExists($bool)
588
    {
589 51
        $this->regenerateEntityIfExists = $bool;
590 51
    }
591
592
    /**
593
     * Sets whether or not to generate stub methods for the entity.
594
     *
595
     * @param bool $bool
596
     *
597
     * @return void
598
     */
599 51
    public function setGenerateStubMethods($bool)
600
    {
601 51
        $this->generateEntityStubMethods = $bool;
602 51
    }
603
604
    /**
605
     * Should an existing entity be backed up if it already exists?
606
     *
607
     * @param bool $bool
608
     *
609
     * @return void
610
     */
611 1
    public function setBackupExisting($bool)
612
    {
613 1
        $this->backupExisting = $bool;
614 1
    }
615
616
    /**
617
     * Set whether or not to add PHP strict types.
618
     *
619
     * @param bool $bool
620
     *
621
     * @return void
622
     */
623 48
    public function setStrictTypes($bool)
624
    {
625 48
        $this->strictTypes = $bool;
626 48
    }
627
628
    /**
629
     * Set whether or not to force type hinting for method result and scalar parameters.
630
     *
631
     * @param bool $bool
632
     *
633
     * @param void
634
     */
635 48
    public function setGenerateMethodsTypeHinting($bool)
636
    {
637 48
        $this->generateMethodsTypeHinting = $bool;
638 48
    }
639
640
    /**
641
     * @param string $type
642
     *
643
     * @return string
644
     */
645 42
    protected function getType($type)
646
    {
647 42
        if (isset($this->typeAlias[$type])) {
648 41
            return $this->typeAlias[$type];
649
        }
650
651 21
        return $type;
652
    }
653
654
    /**
655
     * @return string
656
     */
657 42
    protected function generateEntityStrictTypes()
658
    {
659 42
        if ($this->strictTypes) {
660
            return "\n".'declare(strict_types=1);'."\n";
661
        }
662
663 42
        return '';
664
    }
665
666
    /**
667
     * @param ClassMetadataInfo $metadata
668
     *
669
     * @return string
670
     */
671 42
    protected function generateEntityNamespace(ClassMetadataInfo $metadata)
672
    {
673 42
        if (! $this->hasNamespace($metadata)) {
674 2
            return '';
675
        }
676
677 42
        return 'namespace ' . $this->getNamespace($metadata) .';';
678
    }
679
680
    /**
681
     * @return string
682
     */
683 42
    protected function generateEntityUse()
684
    {
685 42
        if (! $this->generateAnnotations) {
686
            return '';
687
        }
688
689 42
        return "\n".'use Doctrine\ORM\Mapping as ORM;'."\n";
690
    }
691
692
    /**
693
     * @param ClassMetadataInfo $metadata
694
     *
695
     * @return string
696
     */
697 42
    protected function generateEntityClassName(ClassMetadataInfo $metadata)
698
    {
699 42
        return 'class ' . $this->getClassName($metadata) .
700 42
            ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
701
    }
702
703
    /**
704
     * @param ClassMetadataInfo $metadata
705
     *
706
     * @return string
707
     */
708 43
    protected function generateEntityBody(ClassMetadataInfo $metadata)
709
    {
710 43
        $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
711 43
        $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata);
712 43
        $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
713 43
        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
714 43
        $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
715
716 43
        $code = [];
717
718 43
        if ($fieldMappingProperties) {
719 40
            $code[] = $fieldMappingProperties;
720
        }
721
722 43
        if ($embeddedProperties) {
723 10
            $code[] = $embeddedProperties;
724
        }
725
726 43
        if ($associationMappingProperties) {
727 12
            $code[] = $associationMappingProperties;
728
        }
729
730 43
        $code[] = $this->generateEntityConstructor($metadata);
731
732 43
        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...
733 41
            $code[] = $stubMethods;
734
        }
735
736 43
        if ($lifecycleCallbackMethods) {
737 11
            $code[] = $lifecycleCallbackMethods;
738
        }
739
740 43
        return implode("\n", $code);
741
    }
742
743
    /**
744
     * @param ClassMetadataInfo $metadata
745
     *
746
     * @return string
747
     */
748 43
    protected function generateEntityConstructor(ClassMetadataInfo $metadata)
749
    {
750 43
        if ($this->hasMethod('__construct', $metadata)) {
751 2
            return '';
752
        }
753
754 43
        if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) {
755 1
            return $this->generateEmbeddableConstructor($metadata);
756
        }
757
758 42
        $collections = [];
759
760 42
        foreach ($metadata->associationMappings as $mapping) {
761 14
            if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
762 14
                $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
763
            }
764
        }
765
766 42
        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...
767 12
            return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate));
768
        }
769
770 37
        return '';
771
    }
772
773
    /**
774
     * @param ClassMetadataInfo $metadata
775
     *
776
     * @return string
777
     */
778 1
    private function generateEmbeddableConstructor(ClassMetadataInfo $metadata)
779
    {
780 1
        $paramTypes = [];
781 1
        $paramVariables = [];
782 1
        $params = [];
783 1
        $fields = [];
784
785
        // Resort fields to put optional fields at the end of the method signature.
786 1
        $requiredFields = [];
787 1
        $optionalFields = [];
788
789 1
        foreach ($metadata->fieldMappings as $fieldMapping) {
790 1
            if (empty($fieldMapping['nullable'])) {
791 1
                $requiredFields[] = $fieldMapping;
792
793 1
                continue;
794
            }
795
796 1
            $optionalFields[] = $fieldMapping;
797
        }
798
799 1
        $fieldMappings = array_merge($requiredFields, $optionalFields);
800
801 1
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
802 1
            $paramType = '\\' . ltrim($embeddedClass['class'], '\\');
803 1
            $paramVariable = '$' . $fieldName;
804
805 1
            $paramTypes[] = $paramType;
806 1
            $paramVariables[] = $paramVariable;
807 1
            $params[] = $paramType . ' ' . $paramVariable;
808 1
            $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';';
809
        }
810
811 1
        foreach ($fieldMappings as $fieldMapping) {
812 1
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) {
813
                continue;
814
            }
815
816 1
            $paramTypes[] = $this->getType($fieldMapping['type']) . (!empty($fieldMapping['nullable']) ? '|null' : '');
817 1
            $param = '$' . $fieldMapping['fieldName'];
818 1
            $paramVariables[] = $param;
819
820 1
            if ($fieldMapping['type'] === 'datetime') {
821 1
                $param = $this->getType($fieldMapping['type']) . ' ' . $param;
822
            }
823
824 1
            if (!empty($fieldMapping['nullable'])) {
825 1
                $param .= ' = null';
826
            }
827
828 1
            $params[] = $param;
829
830 1
            $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';';
831
        }
832
833 1
        $maxParamTypeLength = max(array_map('strlen', $paramTypes));
834 1
        $paramTags = array_map(
835 1
            function ($type, $variable) use ($maxParamTypeLength) {
836 1
                return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable;
837 1
            },
838 1
            $paramTypes,
839 1
            $paramVariables
840
        );
841
842
        // Generate multi line constructor if the signature exceeds 120 characters.
843 1
        if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) {
844 1
            $delimiter = "\n" . $this->spaces;
845 1
            $params = $delimiter . implode(',' . $delimiter, $params) . "\n";
846
        } else {
847 1
            $params = implode(', ', $params);
848
        }
849
850
        $replacements = [
851 1
            '<paramTags>' => implode("\n * ", $paramTags),
852 1
            '<params>'    => $params,
853 1
            '<fields>'    => implode("\n" . $this->spaces, $fields),
854
        ];
855
856 1
        $constructor = str_replace(
857 1
            array_keys($replacements),
858 1
            array_values($replacements),
859 1
            static::$embeddableConstructorMethodTemplate
860
        );
861
862 1
        return $this->prefixCodeWithSpaces($constructor);
863
    }
864
865
    /**
866
     * @todo this won't work if there is a namespace in brackets and a class outside of it.
867
     *
868
     * @param string $src
869
     *
870
     * @return void
871
     */
872 8
    protected function parseTokensInEntityFile($src)
873
    {
874 8
        $tokens = token_get_all($src);
875 8
        $tokensCount = count($tokens);
876 8
        $lastSeenNamespace = '';
877 8
        $lastSeenClass = false;
878
879 8
        $inNamespace = false;
880 8
        $inClass = false;
881
882 8
        for ($i = 0; $i < $tokensCount; $i++) {
883 8
            $token = $tokens[$i];
884 8
            if (in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
885 8
                continue;
886
            }
887
888 8
            if ($inNamespace) {
889 8
                if (in_array($token[0], [T_NS_SEPARATOR, T_STRING], true)) {
890 8
                    $lastSeenNamespace .= $token[1];
891 8
                } elseif (is_string($token) && in_array($token, [';', '{'], true)) {
892 8
                    $inNamespace = false;
893
                }
894
            }
895
896 8
            if ($inClass) {
897 8
                $inClass = false;
898 8
                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
899 8
                $this->staticReflection[$lastSeenClass]['properties'] = [];
900 8
                $this->staticReflection[$lastSeenClass]['methods'] = [];
901
            }
902
903 8
            if (T_NAMESPACE === $token[0]) {
904 8
                $lastSeenNamespace = '';
905 8
                $inNamespace = true;
906 8
            } elseif (T_CLASS === $token[0] && T_DOUBLE_COLON !== $tokens[$i-1][0]) {
907 8
                $inClass = true;
908 8
            } elseif (T_FUNCTION === $token[0]) {
909 3
                if (T_STRING === $tokens[$i+2][0]) {
910 3
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]);
911
                } elseif ($tokens[$i+2] == '&' && T_STRING === $tokens[$i+3][0]) {
912 3
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]);
913
                }
914 8
            } elseif (in_array($token[0], [T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED], true) && T_FUNCTION !== $tokens[$i+2][0]) {
915 4
                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
916
            }
917
        }
918 8
    }
919
920
    /**
921
     * @param string            $property
922
     * @param ClassMetadataInfo $metadata
923
     *
924
     * @return bool
925
     */
926 42 View Code Duplication
    protected function hasProperty($property, ClassMetadataInfo $metadata)
927
    {
928 42
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
929
            // don't generate property if its already on the base class.
930 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
931 2
            if ($reflClass->hasProperty($property)) {
932 1
                return true;
933
            }
934
        }
935
936
        // check traits for existing property
937 41
        foreach ($this->getTraits($metadata) as $trait) {
938 2
            if ($trait->hasProperty($property)) {
939 2
                return true;
940
            }
941
        }
942
943
        return (
944 41
            isset($this->staticReflection[$metadata->name]) &&
945 41
            in_array($property, $this->staticReflection[$metadata->name]['properties'], true)
946
        );
947
    }
948
949
    /**
950
     * @param string            $method
951
     * @param ClassMetadataInfo $metadata
952
     *
953
     * @return bool
954
     */
955 43 View Code Duplication
    protected function hasMethod($method, ClassMetadataInfo $metadata)
956
    {
957 43
        if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) {
958
            // don't generate method if its already on the base class.
959 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name);
960
961 2
            if ($reflClass->hasMethod($method)) {
962 1
                return true;
963
            }
964
        }
965
966
        // check traits for existing method
967 43
        foreach ($this->getTraits($metadata) as $trait) {
968 2
            if ($trait->hasMethod($method)) {
969 2
                return true;
970
            }
971
        }
972
973
        return (
974 43
            isset($this->staticReflection[$metadata->name]) &&
975 43
            in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods'], true)
976
        );
977
    }
978
979
    /**
980
     * @param ClassMetadataInfo $metadata
981
     *
982
     * @return array
983
     *
984
     * @throws \ReflectionException
985
     */
986 43
    protected function getTraits(ClassMetadataInfo $metadata)
987
    {
988 43
        if (! ($metadata->reflClass !== null || class_exists($metadata->name))) {
989 37
            return [];
990
        }
991
992 7
        $reflClass = $metadata->reflClass ?? new \ReflectionClass($metadata->name);
993
994 7
        $traits = [];
995
996 7
        while ($reflClass !== false) {
997 7
            $traits = array_merge($traits, $reflClass->getTraits());
998
999 7
            $reflClass = $reflClass->getParentClass();
1000
        }
1001
1002 7
        return $traits;
1003
    }
1004
1005
    /**
1006
     * @param ClassMetadataInfo $metadata
1007
     *
1008
     * @return bool
1009
     */
1010 42
    protected function hasNamespace(ClassMetadataInfo $metadata)
1011
    {
1012 42
        return (bool) strpos($metadata->name, '\\');
1013
    }
1014
1015
    /**
1016
     * @return bool
1017
     */
1018 43
    protected function extendsClass()
1019
    {
1020 43
        return (bool) $this->classToExtend;
1021
    }
1022
1023
    /**
1024
     * @return string
1025
     */
1026 2
    protected function getClassToExtend()
1027
    {
1028 2
        return $this->classToExtend;
1029
    }
1030
1031
    /**
1032
     * @return string
1033
     */
1034 1
    protected function getClassToExtendName()
1035
    {
1036 1
        $refl = new \ReflectionClass($this->getClassToExtend());
1037
1038 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...
1039
    }
1040
1041
    /**
1042
     * @param ClassMetadataInfo $metadata
1043
     *
1044
     * @return string
1045
     */
1046 43
    protected function getClassName(ClassMetadataInfo $metadata)
1047
    {
1048 43
        return ($pos = strrpos($metadata->name, '\\'))
1049 43
            ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
1050
    }
1051
1052
    /**
1053
     * @param ClassMetadataInfo $metadata
1054
     *
1055
     * @return string
1056
     */
1057 42
    protected function getNamespace(ClassMetadataInfo $metadata)
1058
    {
1059 42
        return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
1060
    }
1061
1062
    /**
1063
     * @param ClassMetadataInfo $metadata
1064
     *
1065
     * @return string
1066
     */
1067 42
    protected function generateEntityDocBlock(ClassMetadataInfo $metadata)
1068
    {
1069 42
        $lines = [];
1070 42
        $lines[] = '/**';
1071 42
        $lines[] = ' * ' . $this->getClassName($metadata);
1072
1073 42
        if ($this->generateAnnotations) {
1074 42
            $lines[] = ' *';
1075
1076
            $methods = [
1077 42
                'generateTableAnnotation',
1078
                'generateInheritanceAnnotation',
1079
                'generateDiscriminatorColumnAnnotation',
1080
                'generateDiscriminatorMapAnnotation',
1081
                'generateEntityAnnotation',
1082
                'generateEntityListenerAnnotation',
1083
            ];
1084
1085 42
            foreach ($methods as $method) {
1086 42
                if ($code = $this->$method($metadata)) {
1087 42
                    $lines[] = ' * ' . $code;
1088
                }
1089
            }
1090
1091 42
            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...
1092 11
                $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
1093
            }
1094
        }
1095
1096 42
        $lines[] = ' */';
1097
1098 42
        return implode("\n", $lines);
1099
    }
1100
1101
    /**
1102
     * @param ClassMetadataInfo $metadata
1103
     *
1104
     * @return string
1105
     */
1106 42
    protected function generateEntityAnnotation(ClassMetadataInfo $metadata)
1107
    {
1108 42
        $prefix = '@' . $this->annotationsPrefix;
1109
1110 42
        if ($metadata->isEmbeddedClass) {
1111 11
            return $prefix . 'Embeddable';
1112
        }
1113
1114 38
        $customRepository = $metadata->customRepositoryClassName
1115 12
            ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")'
1116 38
            : '';
1117
1118 38
        return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository;
1119
    }
1120
1121
    /**
1122
     * @param ClassMetadataInfo $metadata
1123
     *
1124
     * @return string
1125
     */
1126 42
    protected function generateTableAnnotation(ClassMetadataInfo $metadata)
1127
    {
1128 42
        if ($metadata->isEmbeddedClass) {
1129 11
            return '';
1130
        }
1131
1132 38
        $table = [];
1133
1134 38 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...
1135
            $table[] = 'schema="' . $metadata->table['schema'] . '"';
1136
        }
1137
1138 38 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...
1139 35
            $table[] = 'name="' . $metadata->table['name'] . '"';
1140
        }
1141
1142 38
        if (isset($metadata->table['options']) && $metadata->table['options']) {
1143 1
            $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}';
1144
        }
1145
1146 38 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...
1147 10
            $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
1148 10
            $table[] = 'uniqueConstraints={' . $constraints . '}';
1149
        }
1150
1151 38 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...
1152 10
            $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
1153 10
            $table[] = 'indexes={' . $constraints . '}';
1154
        }
1155
1156 38
        return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
1157
    }
1158
1159
    /**
1160
     * @param string $constraintName
1161
     * @param array  $constraints
1162
     *
1163
     * @return string
1164
     */
1165 10
    protected function generateTableConstraints($constraintName, array $constraints)
1166
    {
1167 10
        $annotations = [];
1168 10
        foreach ($constraints as $name => $constraint) {
1169 10
            $columns = [];
1170 10
            foreach ($constraint['columns'] as $column) {
1171 10
                $columns[] = '"' . $column . '"';
1172
            }
1173 10
            $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
1174
        }
1175
1176 10
        return implode(', ', $annotations);
1177
    }
1178
1179
    /**
1180
     * @param ClassMetadataInfo $metadata
1181
     *
1182
     * @return string
1183
     */
1184 42
    protected function generateInheritanceAnnotation(ClassMetadataInfo $metadata)
1185
    {
1186 42
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1187 42
            return '';
1188
        }
1189
1190
        return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
1191
    }
1192
1193
    /**
1194
     * @param ClassMetadataInfo $metadata
1195
     *
1196
     * @return string
1197
     */
1198 42
    protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $metadata)
1199
    {
1200 42
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1201 42
            return '';
1202
        }
1203
1204
        $discrColumn = $metadata->discriminatorColumn;
1205
        $columnDefinition = 'name="' . $discrColumn['name']
1206
            . '", type="' . $discrColumn['type']
1207
            . '", length=' . $discrColumn['length'];
1208
1209
        return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
1210
    }
1211
1212
    /**
1213
     * @param ClassMetadataInfo $metadata
1214
     *
1215
     * @return string
1216
     */
1217 42
    protected function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata)
1218
    {
1219 42
        if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1220 42
            return null;
1221
        }
1222
1223
        $inheritanceClassMap = [];
1224
1225
        foreach ($metadata->discriminatorMap as $type => $class) {
1226
            $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
1227
        }
1228
1229
        return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
1230
    }
1231
1232
    /**
1233
     * @param ClassMetadataInfo $metadata
1234
     *
1235
     * @return string
1236
     */
1237 42
    protected function generateEntityStubMethods(ClassMetadataInfo $metadata)
1238
    {
1239 42
        $methods = [];
1240
1241 42
        foreach ($metadata->fieldMappings as $fieldMapping) {
1242 41
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) {
1243
                continue;
1244
            }
1245
1246 41
            $nullableField = $this->nullableFieldExpression($fieldMapping);
1247
1248 41
            if ((!$metadata->isEmbeddedClass || !$this->embeddablesImmutable)
1249 41
                && (!isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_NONE)
1250 41
                && $code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)
1251
            ) {
1252 38
                $methods[] = $code;
1253
            }
1254
1255 41
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)) {
1256 41
                $methods[] = $code;
1257
            }
1258
        }
1259
1260 42
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1261 10
            if (isset($embeddedClass['declaredField'])) {
1262 1
                continue;
1263
            }
1264
1265 10
            if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) {
1266 9
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) {
1267 9
                    $methods[] = $code;
1268
                }
1269
            }
1270
1271 10
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) {
1272 10
                $methods[] = $code;
1273
            }
1274
        }
1275
1276 42
        foreach ($metadata->associationMappings as $associationMapping) {
1277 13
            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
1278 12
                $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
1279 12
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1280 10
                    $methods[] = $code;
1281
                }
1282 12
                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1283 12
                    $methods[] = $code;
1284
                }
1285 11
            } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1286 11 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...
1287 11
                    $methods[] = $code;
1288
                }
1289 11 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...
1290 11
                    $methods[] = $code;
1291
                }
1292 11 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...
1293 13
                    $methods[] = $code;
1294
                }
1295
            }
1296
        }
1297
1298 42
        return implode("\n\n", $methods);
1299
    }
1300
1301
    /**
1302
     * @param array $associationMapping
1303
     *
1304
     * @return bool
1305
     */
1306 12
    protected function isAssociationIsNullable(array $associationMapping)
1307
    {
1308 12
        if (isset($associationMapping['id']) && $associationMapping['id']) {
1309
            return false;
1310
        }
1311
1312 12
        if (isset($associationMapping['joinColumns'])) {
1313 2
            $joinColumns = $associationMapping['joinColumns'];
1314
        } else {
1315
            //@todo there is no way to retrieve targetEntity metadata
1316 10
            $joinColumns = [];
1317
        }
1318
1319 12
        foreach ($joinColumns as $joinColumn) {
1320 2
            if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
1321 2
                return false;
1322
            }
1323
        }
1324
1325 12
        return true;
1326
    }
1327
1328
    /**
1329
     * @param ClassMetadataInfo $metadata
1330
     *
1331
     * @return string
1332
     */
1333 43
    protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
1334
    {
1335 43
        if (empty($metadata->lifecycleCallbacks)) {
1336 39
            return '';
1337
        }
1338
1339 11
        $methods = [];
1340
1341 11
        foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
1342 11
            foreach ($callbacks as $callback) {
1343 11
                $methods[] = $this->generateLifecycleCallbackMethod($name, $callback, $metadata);
1344
            }
1345
        }
1346
1347 11
        return implode("\n\n", array_filter($methods));
1348
    }
1349
1350
    /**
1351
     * @param ClassMetadataInfo $metadata
1352
     *
1353
     * @return string
1354
     */
1355 43
    protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
1356
    {
1357 43
        $lines = [];
1358
1359 43
        foreach ($metadata->associationMappings as $associationMapping) {
1360 14
            if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
1361 4
                continue;
1362
            }
1363
1364 12
            $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
1365 12
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
1366 12
                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
1367
        }
1368
1369 43
        return implode("\n", $lines);
1370
    }
1371
1372
    /**
1373
     * @param ClassMetadataInfo $metadata
1374
     *
1375
     * @return string
1376
     */
1377 43
    protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
1378
    {
1379 43
        $lines = [];
1380
1381 43
        foreach ($metadata->fieldMappings as $fieldMapping) {
1382 42
            if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']]) ||
1383 42
                $this->hasProperty($fieldMapping['fieldName'], $metadata) ||
1384 42
                $metadata->isInheritedField($fieldMapping['fieldName'])
1385
            ) {
1386 4
                continue;
1387
            }
1388
1389 40
            $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
1390 40
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
1391 40
                     . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n";
1392
        }
1393
1394 43
        return implode("\n", $lines);
1395
    }
1396
1397
    /**
1398
     * @param ClassMetadataInfo $metadata
1399
     *
1400
     * @return string
1401
     */
1402 43
    protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata)
1403
    {
1404 43
        $lines = [];
1405
1406 43
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1407 10
            if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) {
1408 2
                continue;
1409
            }
1410
1411 10
            $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass);
1412 10
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n";
1413
        }
1414
1415 43
        return implode("\n", $lines);
1416
    }
1417
1418
    /**
1419
     * @param ClassMetadataInfo $metadata
1420
     * @param string            $type
1421
     * @param string            $fieldName
1422
     * @param string|null       $typeHint
1423
     * @param string|null       $defaultValue
1424
     *
1425
     * @return string
1426
     */
1427 41
    protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
1428
    {
1429 41
        $methodName = $type . Inflector::classify($fieldName);
1430 41
        $variableName = Inflector::camelize($fieldName);
1431 41
        if (in_array($type, ["add", "remove"])) {
1432 11
            $methodName = Inflector::singularize($methodName);
1433 11
            $variableName = Inflector::singularize($variableName);
1434
        }
1435
1436 41
        if ($this->hasMethod($methodName, $metadata)) {
1437 5
            return '';
1438
        }
1439 41
        $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName);
1440
1441 41
        $var = sprintf('%sMethodTemplate', $type);
1442 41
        $template = static::$$var;
1443
1444 41
        $methodTypeHint = null;
1445 41
        $types          = Type::getTypesMap();
1446 41
        $variableType   = $typeHint ? $this->getType($typeHint) : null;
1447
1448 41
        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...
1449 15
            $variableType   =  '\\' . ltrim($variableType, '\\');
1450 15
            $methodTypeHint =  '\\' . $typeHint . ' ';
1451
        }
1452
1453 41
        $methodReturnType = null;
1454 41
        if ($this->generateMethodsTypeHinting) {
1455 11
            $methodReturnType = $this->getMethodReturnType($metadata, $type, $fieldName, $variableType);
1456
1457 11
            if (null === $methodTypeHint) {
1458 11
                $type = isset($this->typeHintingAlias[$variableType]) ? $this->typeHintingAlias[$variableType] : $variableType;
1459 11
                $methodTypeHint = $type.' ';
1460
            }
1461
        }
1462
1463
        $replacements = [
1464 41
          '<description>'       => ucfirst($type) . ' ' . $variableName,
1465 41
          '<methodTypeHint>'    => $methodTypeHint,
1466 41
          '<variableType>'      => $variableType . (null !== $defaultValue ? ('|' . $defaultValue) : ''),
1467 41
          '<variableName>'      => $variableName,
1468 41
          '<methodName>'        => $methodName,
1469 41
          '<fieldName>'         => $fieldName,
1470 41
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = ' . $defaultValue) : '',
1471 41
          '<entity>'            => $this->getClassName($metadata),
1472 41
          '<methodTypeReturn>'  => $methodReturnType,
1473
        ];
1474
1475 41
        $method = str_replace(
1476 41
            array_keys($replacements),
1477 41
            array_values($replacements),
1478 41
            $template
1479
        );
1480
1481 41
        return $this->prefixCodeWithSpaces($method);
1482
    }
1483
1484
    /**
1485
     * @param string            $name
1486
     * @param string            $methodName
1487
     * @param ClassMetadataInfo $metadata
1488
     *
1489
     * @return string
1490
     */
1491 11
    protected function generateLifecycleCallbackMethod($name, $methodName, ClassMetadataInfo $metadata)
1492
    {
1493 11
        if ($this->hasMethod($methodName, $metadata)) {
1494 2
            return '';
1495
        }
1496
1497 11
        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
1498
1499
        $replacements = [
1500 11
            '<name>'        => $this->annotationsPrefix . ucfirst($name),
1501 11
            '<methodName>'  => $methodName,
1502
        ];
1503
1504 11
        $method = str_replace(
1505 11
            array_keys($replacements),
1506 11
            array_values($replacements),
1507 11
            static::$lifecycleCallbackMethodTemplate
1508
        );
1509
1510 11
        return $this->prefixCodeWithSpaces($method);
1511
    }
1512
1513
    /**
1514
     * @param array $joinColumn
1515
     *
1516
     * @return string
1517
     */
1518 12
    protected function generateJoinColumnAnnotation(array $joinColumn)
1519
    {
1520 12
        $joinColumnAnnot = [];
1521
1522 12
        if (isset($joinColumn['name'])) {
1523 12
            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
1524
        }
1525
1526 12
        if (isset($joinColumn['referencedColumnName'])) {
1527 12
            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
1528
        }
1529
1530 12
        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
1531 1
            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
1532
        }
1533
1534 12
        if (isset($joinColumn['nullable'])) {
1535 1
            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
1536
        }
1537
1538 12
        if (isset($joinColumn['onDelete'])) {
1539 1
            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
1540
        }
1541
1542 12
        if (isset($joinColumn['columnDefinition'])) {
1543 1
            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
1544
        }
1545
1546 12
        return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
1547
    }
1548
1549
    /**
1550
     * @param array             $associationMapping
1551
     * @param ClassMetadataInfo $metadata
1552
     *
1553
     * @return string
1554
     */
1555 12
    protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
1556
    {
1557 12
        $lines = [];
1558 12
        $lines[] = $this->spaces . '/**';
1559
1560 12
        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1561 12
            $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection';
1562
        } else {
1563 11
            $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\');
1564
        }
1565
1566 12
        if ($this->generateAnnotations) {
1567 12
            $lines[] = $this->spaces . ' *';
1568
1569 12
            if (isset($associationMapping['id']) && $associationMapping['id']) {
1570
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1571
1572 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...
1573
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1574
                }
1575
            }
1576
1577 12
            $type = null;
1578 12
            switch ($associationMapping['type']) {
1579 12
                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...
1580 11
                    $type = 'OneToOne';
1581 11
                    break;
1582 12
                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...
1583 1
                    $type = 'ManyToOne';
1584 1
                    break;
1585 12
                case ClassMetadataInfo::ONE_TO_MANY:
1586 1
                    $type = 'OneToMany';
1587 1
                    break;
1588 12
                case ClassMetadataInfo::MANY_TO_MANY:
1589 12
                    $type = 'ManyToMany';
1590 12
                    break;
1591
            }
1592 12
            $typeOptions = [];
1593
1594 12
            if (isset($associationMapping['targetEntity'])) {
1595 12
                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
1596
            }
1597
1598 12
            if (isset($associationMapping['inversedBy'])) {
1599 1
                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
1600
            }
1601
1602 12
            if (isset($associationMapping['mappedBy'])) {
1603 11
                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
1604
            }
1605
1606 12
            if ($associationMapping['cascade']) {
1607 1
                $cascades = [];
1608
1609 1
                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
1610 1
                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
1611 1
                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
1612 1
                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
1613 1
                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
1614
1615 1
                if (count($cascades) === 5) {
1616 1
                    $cascades = ['"all"'];
1617
                }
1618
1619 1
                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
1620
            }
1621
1622 12
            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
1623 1
                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
1624
            }
1625
1626 12
            if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) {
1627
                $fetchMap = [
1628 11
                    ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY',
1629
                    ClassMetadataInfo::FETCH_EAGER      => 'EAGER',
1630
                ];
1631
1632 11
                $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"';
1633
            }
1634
1635 12
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
1636
1637 12
            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
1638 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
1639
1640 1
                $joinColumnsLines = [];
1641
1642 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...
1643 1
                    if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
1644 1
                        $joinColumnsLines[] = $this->spaces . ' *   ' . $joinColumnAnnot;
1645
                    }
1646
                }
1647
1648 1
                $lines[] = implode(",\n", $joinColumnsLines);
1649 1
                $lines[] = $this->spaces . ' * })';
1650
            }
1651
1652 12
            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
1653 12
                $joinTable = [];
1654 12
                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
1655
1656 12
                if (isset($associationMapping['joinTable']['schema'])) {
1657
                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
1658
                }
1659
1660 12
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
1661 12
                $lines[] = $this->spaces . ' *   joinColumns={';
1662
1663 12
                $joinColumnsLines = [];
1664
1665 12 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...
1666 12
                    $joinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1667
                }
1668
1669 12
                $lines[] = implode(",". PHP_EOL, $joinColumnsLines);
1670 12
                $lines[] = $this->spaces . ' *   },';
1671 12
                $lines[] = $this->spaces . ' *   inverseJoinColumns={';
1672
1673 12
                $inverseJoinColumnsLines = [];
1674
1675 12
                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
1676 12
                    $inverseJoinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1677
                }
1678
1679 12
                $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines);
1680 12
                $lines[] = $this->spaces . ' *   }';
1681 12
                $lines[] = $this->spaces . ' * )';
1682
            }
1683
1684 12
            if (isset($associationMapping['orderBy'])) {
1685 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
1686
1687 1
                foreach ($associationMapping['orderBy'] as $name => $direction) {
1688 1
                    $lines[] = $this->spaces . ' *     "' . $name . '"="' . $direction . '",';
1689
                }
1690
1691 1
                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
1692 1
                $lines[] = $this->spaces . ' * })';
1693
            }
1694
        }
1695
1696 12
        $lines[] = $this->spaces . ' */';
1697
1698 12
        return implode("\n", $lines);
1699
    }
1700
1701
    /**
1702
     * @param array             $fieldMapping
1703
     * @param ClassMetadataInfo $metadata
1704
     *
1705
     * @return string
1706
     */
1707 40
    protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
1708
    {
1709 40
        $lines = [];
1710 40
        $lines[] = $this->spaces . '/**';
1711 40
        $lines[] = $this->spaces . ' * @var '
1712 40
            . $this->getType($fieldMapping['type'])
1713 40
            . ($this->nullableFieldExpression($fieldMapping) ? '|null' : '');
1714
1715 40
        if ($this->generateAnnotations) {
1716 40
            $lines[] = $this->spaces . ' *';
1717
1718 40
            $column = [];
1719 40
            if (isset($fieldMapping['columnName'])) {
1720 40
                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1721
            }
1722
1723 40
            if (isset($fieldMapping['type'])) {
1724 40
                $column[] = 'type="' . $fieldMapping['type'] . '"';
1725
            }
1726
1727 40
            if (isset($fieldMapping['length'])) {
1728 4
                $column[] = 'length=' . $fieldMapping['length'];
1729
            }
1730
1731 40
            if (isset($fieldMapping['precision'])) {
1732 4
                $column[] = 'precision=' .  $fieldMapping['precision'];
1733
            }
1734
1735 40
            if (isset($fieldMapping['scale'])) {
1736 4
                $column[] = 'scale=' . $fieldMapping['scale'];
1737
            }
1738
1739 40 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...
1740 10
                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
1741
            }
1742
1743 40
            $options = [];
1744
1745 40
            if (isset($fieldMapping['options']['unsigned']) && $fieldMapping['options']['unsigned']) {
1746 1
                $options[] = '"unsigned"=true';
1747
            }
1748
1749 40
            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...
1750 1
                $column[] = 'options={'.implode(',', $options).'}';
1751
            }
1752
1753 40
            if (isset($fieldMapping['columnDefinition'])) {
1754 1
                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1755
            }
1756
1757 40 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...
1758 4
                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1759
            }
1760
1761 40
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1762
1763 40
            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1764 36
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1765
1766 36 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...
1767 36
                    $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1768
                }
1769
1770 36
                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...
1771 1
                    $sequenceGenerator = [];
1772
1773 1
                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1774 1
                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1775
                    }
1776
1777 1
                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1778 1
                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1779
                    }
1780
1781 1
                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1782 1
                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1783
                    }
1784
1785 1
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1786
                }
1787
            }
1788
1789 40
            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1790
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
1791
            }
1792
        }
1793
1794 40
        $lines[] = $this->spaces . ' */';
1795
1796 40
        return implode("\n", $lines);
1797
    }
1798
1799
    /**
1800
     * @param array $embeddedClass
1801
     *
1802
     * @return string
1803
     */
1804 10
    protected function generateEmbeddedPropertyDocBlock(array $embeddedClass)
1805
    {
1806 10
        $lines = [];
1807 10
        $lines[] = $this->spaces . '/**';
1808 10
        $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\');
1809
1810 10
        if ($this->generateAnnotations) {
1811 10
            $lines[] = $this->spaces . ' *';
1812
1813 10
            $embedded = ['class="' . $embeddedClass['class'] . '"'];
1814
1815 10
            if (isset($embeddedClass['columnPrefix'])) {
1816 8
                if (is_string($embeddedClass['columnPrefix'])) {
1817 1
                    $embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"';
1818
                } else {
1819 7
                    $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
1820
                }
1821
            }
1822
1823 10
            $lines[] = $this->spaces . ' * @' .
1824 10
                $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')';
1825
        }
1826
1827 10
        $lines[] = $this->spaces . ' */';
1828
1829 10
        return implode("\n", $lines);
1830
    }
1831
1832 42
    private function generateEntityListenerAnnotation(ClassMetadataInfo $metadata): string
1833
    {
1834 42
        if (0 === \count($metadata->entityListeners)) {
1835 41
            return '';
1836
        }
1837
1838 1
        $processedClasses = [];
1839 1
        foreach ($metadata->entityListeners as $event => $eventListeners) {
1840 1
            foreach ($eventListeners as $eventListener) {
1841 1
                $processedClasses[] = '"' . $eventListener['class'] . '"';
1842
            }
1843
        }
1844
1845 1
        return \sprintf(
1846 1
            '%s%s({%s})',
1847 1
            '@' . $this->annotationsPrefix,
1848 1
            'EntityListeners',
1849 1
            \implode(',', \array_unique($processedClasses))
1850
        );
1851
    }
1852
1853
    /**
1854
     * @param string $code
1855
     * @param int    $num
1856
     *
1857
     * @return string
1858
     */
1859 42
    protected function prefixCodeWithSpaces($code, $num = 1)
1860
    {
1861 42
        $lines = explode("\n", $code);
1862
1863 42
        foreach ($lines as $key => $value) {
1864 42
            if ( ! empty($value)) {
1865 42
                $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1866
            }
1867
        }
1868
1869 42
        return implode("\n", $lines);
1870
    }
1871
1872
    /**
1873
     * @param integer $type The inheritance type used by the class and its subclasses.
1874
     *
1875
     * @return string The literal string for the inheritance type.
1876
     *
1877
     * @throws \InvalidArgumentException When the inheritance type does not exist.
1878
     */
1879 1
    protected function getInheritanceTypeString($type)
1880
    {
1881 1
        if ( ! isset(static::$inheritanceTypeMap[$type])) {
1882 1
            throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type));
1883
        }
1884
1885 1
        return static::$inheritanceTypeMap[$type];
1886
    }
1887
1888
    /**
1889
     * @param integer $type The policy used for change-tracking for the mapped class.
1890
     *
1891
     * @return string The literal string for the change-tracking type.
1892
     *
1893
     * @throws \InvalidArgumentException When the change-tracking type does not exist.
1894
     */
1895 1
    protected function getChangeTrackingPolicyString($type)
1896
    {
1897 1
        if ( ! isset(static::$changeTrackingPolicyMap[$type])) {
1898 1
            throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type));
1899
        }
1900
1901 1
        return static::$changeTrackingPolicyMap[$type];
1902
    }
1903
1904
    /**
1905
     * @param integer $type The generator to use for the mapped class.
1906
     *
1907
     * @return string The literal string for the generator type.
1908
     *
1909
     * @throws \InvalidArgumentException    When the generator type does not exist.
1910
     */
1911 37
    protected function getIdGeneratorTypeString($type)
1912
    {
1913 37
        if ( ! isset(static::$generatorStrategyMap[$type])) {
1914 1
            throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type));
1915
        }
1916
1917 37
        return static::$generatorStrategyMap[$type];
1918
    }
1919
1920
    /**
1921
     * @param array $fieldMapping
1922
     *
1923
     * @return string|null
1924
     */
1925 42
    private function nullableFieldExpression(array $fieldMapping)
1926
    {
1927 42
        if (isset($fieldMapping['nullable']) && true === $fieldMapping['nullable']) {
1928 7
            return 'null';
1929
        }
1930
1931 42
        return null;
1932
    }
1933
1934
    /**
1935
     * Exports (nested) option elements.
1936
     *
1937
     * @param array $options
1938
     *
1939
     * @return string
1940
     */
1941 1
    private function exportTableOptions(array $options)
1942
    {
1943 1
        $optionsStr = [];
1944
1945 1
        foreach ($options as $name => $option) {
1946 1
            if (is_array($option)) {
1947 1
                $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}';
1948
            } else {
1949 1
                $optionsStr[] = '"' . $name . '"="' . (string) $option . '"';
1950
            }
1951
        }
1952
1953 1
        return implode(',', $optionsStr);
1954
    }
1955
1956
    /**
1957
     * @param ClassMetadataInfo $metadata
1958
     * @param string            $type
1959
     * @param string            $fieldName
1960
     * @param string            $variableType
1961
     * @return string
1962
     */
1963 11
    private function getMethodReturnType(ClassMetadataInfo $metadata, $type, $fieldName, $variableType)
1964
    {
1965 11
        if (in_array($type, array('set', 'add'))) {
1966 11
            return ': self';
1967
        }
1968
1969 11
        if ('get' === $type) {
1970
            if (
1971 11
                $metadata->isSingleValuedAssociation($fieldName) ||
1972 11
                (!$metadata->hasAssociation($fieldName) && $metadata->isNullable($fieldName))
1973
            ) {
1974 1
                return null;
1975
            }
1976
1977 11
            $type = isset($this->typeHintingAlias[$variableType]) ? $this->typeHintingAlias[$variableType] : $variableType;
1978 11
            return sprintf(': %s', $type);
1979
        }
1980
1981 1
        if ('remove' === $type) {
1982 1
            return ': bool';
1983
        }
1984
1985
        return null;
1986
    }
1987
}
1988