Failed Conditions
Pull Request — master (#6392)
by Alessandro
11:37
created

generateEmbeddedPropertyDocBlock()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 15
cts 15
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 4
nop 1
crap 4
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 11
                $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
                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
                    $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
            ];
1014
1015 31
            foreach ($methods as $method) {
1016 31
                if ($code = $this->$method($metadata)) {
1017 31
                    $lines[] = ' * ' . $code;
1018
                }
1019
            }
1020
1021 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...
1022 10
                $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
1023
            }
1024
        }
1025
1026 31
        $lines[] = ' */';
1027
1028 31
        return implode("\n", $lines);
1029
    }
1030
1031
    /**
1032
     * @param ClassMetadataInfo $metadata
1033
     *
1034
     * @return string
1035
     */
1036 31
    protected function generateEntityAnnotation(ClassMetadataInfo $metadata)
1037
    {
1038 31
        $prefix = '@' . $this->annotationsPrefix;
1039
1040 31
        if ($metadata->isEmbeddedClass) {
1041 11
            return $prefix . 'Embeddable';
1042
        }
1043
1044 27
        $customRepository = $metadata->customRepositoryClassName
1045 11
            ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")'
1046 27
            : '';
1047
1048 27
        return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository;
1049
    }
1050
1051
    /**
1052
     * @param ClassMetadataInfo $metadata
1053
     *
1054
     * @return string
1055
     */
1056 31
    protected function generateTableAnnotation(ClassMetadataInfo $metadata)
1057
    {
1058 31
        if ($metadata->isEmbeddedClass) {
1059 11
            return '';
1060
        }
1061
1062 27
        $table = [];
1063
1064 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...
1065
            $table[] = 'schema="' . $metadata->table['schema'] . '"';
1066
        }
1067
1068 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...
1069 24
            $table[] = 'name="' . $metadata->table['name'] . '"';
1070
        }
1071
1072 27
        if (isset($metadata->table['options']) && $metadata->table['options']) {
1073 1
            $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}';
1074
        }
1075
1076 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...
1077 9
            $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
1078 9
            $table[] = 'uniqueConstraints={' . $constraints . '}';
1079
        }
1080
1081 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...
1082 9
            $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
1083 9
            $table[] = 'indexes={' . $constraints . '}';
1084
        }
1085
1086 27
        return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
1087
    }
1088
1089
    /**
1090
     * @param string $constraintName
1091
     * @param array  $constraints
1092
     *
1093
     * @return string
1094
     */
1095 9
    protected function generateTableConstraints($constraintName, array $constraints)
1096
    {
1097 9
        $annotations = [];
1098 9
        foreach ($constraints as $name => $constraint) {
1099 9
            $columns = [];
1100 9
            foreach ($constraint['columns'] as $column) {
1101 9
                $columns[] = '"' . $column . '"';
1102
            }
1103 9
            $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
1104
        }
1105
1106 9
        return implode(', ', $annotations);
1107
    }
1108
1109
    /**
1110
     * @param ClassMetadataInfo $metadata
1111
     *
1112
     * @return string
1113
     */
1114 31
    protected function generateInheritanceAnnotation(ClassMetadataInfo $metadata)
1115
    {
1116 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1117
            return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
1118
        }
1119 31
    }
1120
1121
    /**
1122
     * @param ClassMetadataInfo $metadata
1123
     *
1124
     * @return string
1125
     */
1126 31
    protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $metadata)
1127
    {
1128 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1129
            $discrColumn = $metadata->discriminatorColumn;
1130
            $columnDefinition = 'name="' . $discrColumn['name']
1131
                . '", type="' . $discrColumn['type']
1132
                . '", length=' . $discrColumn['length'];
1133
1134
            return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
1135
        }
1136 31
    }
1137
1138
    /**
1139
     * @param ClassMetadataInfo $metadata
1140
     *
1141
     * @return string
1142
     */
1143 31
    protected function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata)
1144
    {
1145 31
        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
1146
            $inheritanceClassMap = [];
1147
1148
            foreach ($metadata->discriminatorMap as $type => $class) {
1149
                $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
1150
            }
1151
1152
            return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
1153
        }
1154 31
    }
1155
1156
    /**
1157
     * @param ClassMetadataInfo $metadata
1158
     *
1159
     * @return string
1160
     */
1161 31
    protected function generateEntityStubMethods(ClassMetadataInfo $metadata)
1162
    {
1163 31
        $methods = [];
1164
1165 31
        foreach ($metadata->fieldMappings as $fieldMapping) {
1166 30
            if (isset($fieldMapping['declaredField']) &&
1167
                isset($metadata->embeddedClasses[$fieldMapping['declaredField']])
1168
            ) {
1169
                continue;
1170
            }
1171
1172 30
            $nullableField = $this->nullableFieldExpression($fieldMapping);
1173
1174 30
            if (( ! isset($fieldMapping['id']) ||
1175 26
                    ! $fieldMapping['id'] ||
1176 26
                    $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE
1177 28
                ) && (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable)
1178 27
                && $code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)
1179
            ) {
1180 27
                $methods[] = $code;
1181
            }
1182
1183 30
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'], $nullableField)) {
1184 30
                $methods[] = $code;
1185
            }
1186
        }
1187
1188 31
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1189 10
            if (isset($embeddedClass['declaredField'])) {
1190 1
                continue;
1191
            }
1192
1193 10
            if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) {
1194 9
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) {
1195 9
                    $methods[] = $code;
1196
                }
1197
            }
1198
1199 10
            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) {
1200 10
                $methods[] = $code;
1201
            }
1202
        }
1203
1204 31
        foreach ($metadata->associationMappings as $associationMapping) {
1205 12
            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
1206 11
                $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
1207 11
                if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1208 9
                    $methods[] = $code;
1209
                }
1210 11
                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
1211 9
                    $methods[] = $code;
1212
                }
1213 10
            } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1214 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...
1215 10
                    $methods[] = $code;
1216
                }
1217 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...
1218 10
                    $methods[] = $code;
1219
                }
1220 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...
1221 10
                    $methods[] = $code;
1222
                }
1223
            }
1224
        }
1225
1226 31
        return implode("\n\n", $methods);
1227
    }
1228
1229
    /**
1230
     * @param array $associationMapping
1231
     *
1232
     * @return bool
1233
     */
1234 11
    protected function isAssociationIsNullable(array $associationMapping)
1235
    {
1236 11
        if (isset($associationMapping['id']) && $associationMapping['id']) {
1237
            return false;
1238
        }
1239
1240 11
        if (isset($associationMapping['joinColumns'])) {
1241 2
            $joinColumns = $associationMapping['joinColumns'];
1242
        } else {
1243
            //@todo there is no way to retrieve targetEntity metadata
1244 9
            $joinColumns = [];
1245
        }
1246
1247 11
        foreach ($joinColumns as $joinColumn) {
1248 2
            if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
1249
                return false;
1250
            }
1251
        }
1252
1253 11
        return true;
1254
    }
1255
1256
    /**
1257
     * @param ClassMetadataInfo $metadata
1258
     *
1259
     * @return string
1260
     */
1261 32
    protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
1262
    {
1263 32
        if (empty($metadata->lifecycleCallbacks)) {
1264 29
            return '';
1265
        }
1266
1267 10
        $methods = [];
1268
1269 10
        foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
1270 10
            foreach ($callbacks as $callback) {
1271 10
                $methods[] = $this->generateLifecycleCallbackMethod($name, $callback, $metadata);
1272
            }
1273
        }
1274
1275 10
        return implode("\n\n", array_filter($methods));
1276
    }
1277
1278
    /**
1279
     * @param ClassMetadataInfo $metadata
1280
     *
1281
     * @return string
1282
     */
1283 32
    protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
1284
    {
1285 32
        $lines = [];
1286
1287 32
        foreach ($metadata->associationMappings as $associationMapping) {
1288 13
            if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
1289 4
                continue;
1290
            }
1291
1292 11
            $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
1293 11
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
1294 11
                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
1295
        }
1296
1297 32
        return implode("\n", $lines);
1298
    }
1299
1300
    /**
1301
     * @param ClassMetadataInfo $metadata
1302
     *
1303
     * @return string
1304
     */
1305 32
    protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
1306
    {
1307 32
        $lines = [];
1308
1309 32
        foreach ($metadata->fieldMappings as $fieldMapping) {
1310 31
            if ($this->hasProperty($fieldMapping['fieldName'], $metadata) ||
1311 30
                $metadata->isInheritedField($fieldMapping['fieldName']) ||
1312
                (
1313 29
                    isset($fieldMapping['declaredField']) &&
1314
                    isset($metadata->embeddedClasses[$fieldMapping['declaredField']])
1315
                )
1316
            ) {
1317 4
                continue;
1318
            }
1319
1320 29
            $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
1321 29
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
1322 29
                     . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n";
1323
        }
1324
1325 32
        return implode("\n", $lines);
1326
    }
1327
1328
    /**
1329
     * @param ClassMetadataInfo $metadata
1330
     *
1331
     * @return string
1332
     */
1333 32
    protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata)
1334
    {
1335 32
        $lines = [];
1336
1337 32
        foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) {
1338 10
            if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) {
1339 2
                continue;
1340
            }
1341
1342 10
            $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass);
1343 10
            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n";
1344
        }
1345
1346 32
        return implode("\n", $lines);
1347
    }
1348
1349
    /**
1350
     * @param ClassMetadataInfo $metadata
1351
     * @param string            $type
1352
     * @param string            $fieldName
1353
     * @param string|null       $typeHint
1354
     * @param string|null       $defaultValue
1355
     *
1356
     * @return string
1357
     */
1358 30
    protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
1359
    {
1360 30
        $methodName = $type . Inflector::classify($fieldName);
1361 30
        $variableName = Inflector::camelize($fieldName);
1362 30
        if (in_array($type, ["add", "remove"])) {
1363 10
            $methodName = Inflector::singularize($methodName);
1364 10
            $variableName = Inflector::singularize($variableName);
1365
        }
1366
1367 30
        if ($this->hasMethod($methodName, $metadata)) {
1368 5
            return '';
1369
        }
1370 30
        $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName);
1371
1372 30
        $var = sprintf('%sMethodTemplate', $type);
1373 30
        $template = static::$$var;
1374
1375 30
        $methodTypeHint = null;
1376 30
        $types          = Type::getTypesMap();
1377 30
        $variableType   = $typeHint ? $this->getType($typeHint) : null;
1378
1379 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...
1380 14
            $variableType   =  '\\' . ltrim($variableType, '\\');
1381 14
            $methodTypeHint =  '\\' . $typeHint . ' ';
1382
        }
1383
1384
        $replacements = [
1385 30
          '<description>'       => ucfirst($type) . ' ' . $variableName . '.',
1386 30
          '<methodTypeHint>'    => $methodTypeHint,
1387 30
          '<variableType>'      => $variableType . (null !== $defaultValue ? ('|' . $defaultValue) : ''),
1388 30
          '<variableName>'      => $variableName,
1389 30
          '<methodName>'        => $methodName,
1390 30
          '<fieldName>'         => $fieldName,
1391 30
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = ' . $defaultValue) : '',
1392 30
          '<entity>'            => $this->getClassName($metadata)
1393
        ];
1394
1395 30
        $method = str_replace(
1396 30
            array_keys($replacements),
1397 30
            array_values($replacements),
1398 30
            $template
1399
        );
1400
1401 30
        return $this->prefixCodeWithSpaces($method);
1402
    }
1403
1404
    /**
1405
     * @param string            $name
1406
     * @param string            $methodName
1407
     * @param ClassMetadataInfo $metadata
1408
     *
1409
     * @return string
1410
     */
1411 10
    protected function generateLifecycleCallbackMethod($name, $methodName, ClassMetadataInfo $metadata)
1412
    {
1413 10
        if ($this->hasMethod($methodName, $metadata)) {
1414 2
            return '';
1415
        }
1416 10
        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
1417
1418
        $replacements = [
1419 10
            '<name>'        => $this->annotationsPrefix . ucfirst($name),
1420 10
            '<methodName>'  => $methodName,
1421
        ];
1422
1423 10
        $method = str_replace(
1424 10
            array_keys($replacements),
1425 10
            array_values($replacements),
1426 10
            static::$lifecycleCallbackMethodTemplate
1427
        );
1428
1429 10
        return $this->prefixCodeWithSpaces($method);
1430
    }
1431
1432
    /**
1433
     * @param array $joinColumn
1434
     *
1435
     * @return string
1436
     */
1437 11
    protected function generateJoinColumnAnnotation(array $joinColumn)
1438
    {
1439 11
        $joinColumnAnnot = [];
1440
1441 11
        if (isset($joinColumn['name'])) {
1442 11
            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
1443
        }
1444
1445 11
        if (isset($joinColumn['referencedColumnName'])) {
1446 11
            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
1447
        }
1448
1449 11
        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
1450 1
            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
1451
        }
1452
1453 11
        if (isset($joinColumn['nullable'])) {
1454 1
            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
1455
        }
1456
1457 11
        if (isset($joinColumn['onDelete'])) {
1458 1
            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
1459
        }
1460
1461 11
        if (isset($joinColumn['columnDefinition'])) {
1462 1
            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
1463
        }
1464
1465 11
        return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
1466
    }
1467
1468
    /**
1469
     * @param array             $associationMapping
1470
     * @param ClassMetadataInfo $metadata
1471
     *
1472
     * @return string
1473
     */
1474 11
    protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
1475
    {
1476 11
        $lines = [];
1477 11
        $lines[] = $this->spaces . '/**';
1478
1479 11
        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
1480 11
            $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection';
1481
        } else {
1482 10
            $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\');
1483
        }
1484
1485 11
        if ($this->generateAnnotations) {
1486 11
            $lines[] = $this->spaces . ' *';
1487
1488 11
            if (isset($associationMapping['id']) && $associationMapping['id']) {
1489
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1490
1491 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...
1492
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1493
                }
1494
            }
1495
1496 11
            $type = null;
1497 11
            switch ($associationMapping['type']) {
1498 11
                case ClassMetadataInfo::ONE_TO_ONE:
1499 10
                    $type = 'OneToOne';
1500 10
                    break;
1501 11
                case ClassMetadataInfo::MANY_TO_ONE:
1502 1
                    $type = 'ManyToOne';
1503 1
                    break;
1504 11
                case ClassMetadataInfo::ONE_TO_MANY:
1505 1
                    $type = 'OneToMany';
1506 1
                    break;
1507 11
                case ClassMetadataInfo::MANY_TO_MANY:
1508 11
                    $type = 'ManyToMany';
1509 11
                    break;
1510
            }
1511 11
            $typeOptions = [];
1512
1513 11
            if (isset($associationMapping['targetEntity'])) {
1514 11
                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
1515
            }
1516
1517 11
            if (isset($associationMapping['inversedBy'])) {
1518 1
                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
1519
            }
1520
1521 11
            if (isset($associationMapping['mappedBy'])) {
1522 10
                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
1523
            }
1524
1525 11
            if ($associationMapping['cascade']) {
1526 1
                $cascades = [];
1527
1528 1
                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
1529 1
                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
1530 1
                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
1531 1
                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
1532 1
                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
1533
1534 1
                if (count($cascades) === 5) {
1535 1
                    $cascades = ['"all"'];
1536
                }
1537
1538 1
                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
1539
            }
1540
1541 11
            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
1542 1
                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
1543
            }
1544
1545 11
            if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) {
1546
                $fetchMap = [
1547 10
                    ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY',
1548 10
                    ClassMetadataInfo::FETCH_EAGER      => 'EAGER',
1549
                ];
1550
1551 10
                $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"';
1552
            }
1553
1554 11
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
1555
1556 11
            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
1557 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
1558
1559 1
                $joinColumnsLines = [];
1560
1561 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...
1562 1
                    if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
1563 1
                        $joinColumnsLines[] = $this->spaces . ' *   ' . $joinColumnAnnot;
1564
                    }
1565
                }
1566
1567 1
                $lines[] = implode(",\n", $joinColumnsLines);
1568 1
                $lines[] = $this->spaces . ' * })';
1569
            }
1570
1571 11
            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
1572 11
                $joinTable = [];
1573 11
                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
1574
1575 11
                if (isset($associationMapping['joinTable']['schema'])) {
1576
                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
1577
                }
1578
1579 11
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
1580 11
                $lines[] = $this->spaces . ' *   joinColumns={';
1581
1582 11
                $joinColumnsLines = [];
1583
1584 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...
1585 11
                    $joinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1586
                }
1587
1588 11
                $lines[] = implode(",". PHP_EOL, $joinColumnsLines);
1589 11
                $lines[] = $this->spaces . ' *   },';
1590 11
                $lines[] = $this->spaces . ' *   inverseJoinColumns={';
1591
1592 11
                $inverseJoinColumnsLines = [];
1593
1594 11
                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
1595 11
                    $inverseJoinColumnsLines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
1596
                }
1597
1598 11
                $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines);
1599 11
                $lines[] = $this->spaces . ' *   }';
1600 11
                $lines[] = $this->spaces . ' * )';
1601
            }
1602
1603 11
            if (isset($associationMapping['orderBy'])) {
1604 1
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
1605
1606 1
                foreach ($associationMapping['orderBy'] as $name => $direction) {
1607 1
                    $lines[] = $this->spaces . ' *     "' . $name . '"="' . $direction . '",';
1608
                }
1609
1610 1
                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
1611 1
                $lines[] = $this->spaces . ' * })';
1612
            }
1613
        }
1614
1615 11
        $lines[] = $this->spaces . ' */';
1616
1617 11
        return implode("\n", $lines);
1618
    }
1619
1620
    /**
1621
     * @param array             $fieldMapping
1622
     * @param ClassMetadataInfo $metadata
1623
     *
1624
     * @return string
1625
     */
1626 29
    protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
1627
    {
1628 29
        $lines = [];
1629 29
        $lines[] = $this->spaces . '/**';
1630 29
        $lines[] = $this->spaces . ' * @var '
1631 29
            . $this->getType($fieldMapping['type'])
1632 29
            . ($this->nullableFieldExpression($fieldMapping) ? '|null' : '');
1633
1634 29
        if ($this->generateAnnotations) {
1635 29
            $lines[] = $this->spaces . ' *';
1636
1637 29
            $column = [];
1638 29
            if (isset($fieldMapping['columnName'])) {
1639 29
                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1640
            }
1641
1642 29
            if (isset($fieldMapping['type'])) {
1643 29
                $column[] = 'type="' . $fieldMapping['type'] . '"';
1644
            }
1645
1646 29
            if (isset($fieldMapping['length'])) {
1647 4
                $column[] = 'length=' . $fieldMapping['length'];
1648
            }
1649
1650 29
            if (isset($fieldMapping['precision'])) {
1651 4
                $column[] = 'precision=' .  $fieldMapping['precision'];
1652
            }
1653
1654 29
            if (isset($fieldMapping['scale'])) {
1655 4
                $column[] = 'scale=' . $fieldMapping['scale'];
1656
            }
1657
1658 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...
1659 10
                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
1660
            }
1661
1662 29
            $options = [];
1663
1664 29
            if (isset($fieldMapping['options']['unsigned']) && $fieldMapping['options']['unsigned']) {
1665 1
                $options[] = '"unsigned"=true';
1666
            }
1667
1668 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...
1669 1
                $column[] = 'options={'.implode(',', $options).'}';
1670
            }
1671
1672 29
            if (isset($fieldMapping['columnDefinition'])) {
1673 1
                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1674
            }
1675
1676 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...
1677 4
                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1678
            }
1679
1680 29
            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1681
1682 29
            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1683 25
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1684
1685 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...
1686 25
                    $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1687
                }
1688
1689 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...
1690 1
                    $sequenceGenerator = [];
1691
1692 1
                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1693 1
                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1694
                    }
1695
1696 1
                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1697 1
                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1698
                    }
1699
1700 1
                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1701 1
                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1702
                    }
1703
1704 1
                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1705
                }
1706
            }
1707
1708 29
            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1709
                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
1710
            }
1711
        }
1712
1713 29
        $lines[] = $this->spaces . ' */';
1714
1715 29
        return implode("\n", $lines);
1716
    }
1717
1718
    /**
1719
     * @param array $embeddedClass
1720
     *
1721
     * @return string
1722
     */
1723 10
    protected function generateEmbeddedPropertyDocBlock(array $embeddedClass)
1724
    {
1725 10
        $lines = [];
1726 10
        $lines[] = $this->spaces . '/**';
1727 10
        $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\');
1728
1729 10
        if ($this->generateAnnotations) {
1730 10
            $lines[] = $this->spaces . ' *';
1731
1732 10
            $embedded = ['class="' . $embeddedClass['class'] . '"'];
1733
1734 10
            if (isset($embeddedClass['columnPrefix'])) {
1735 8
                if (is_string($embeddedClass['columnPrefix'])) {
1736 1
                    $embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"';
1737
                } else {
1738 7
                    $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
1739
                }
1740
            }
1741
1742 10
            $lines[] = $this->spaces . ' * @' .
1743 10
                $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')';
1744
        }
1745
1746 10
        $lines[] = $this->spaces . ' */';
1747
1748 10
        return implode("\n", $lines);
1749
    }
1750
1751
    /**
1752
     * @param string $code
1753
     * @param int    $num
1754
     *
1755
     * @return string
1756
     */
1757 31
    protected function prefixCodeWithSpaces($code, $num = 1)
1758
    {
1759 31
        $lines = explode("\n", $code);
1760
1761 31
        foreach ($lines as $key => $value) {
1762 31
            if ( ! empty($value)) {
1763 31
                $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1764
            }
1765
        }
1766
1767 31
        return implode("\n", $lines);
1768
    }
1769
1770
    /**
1771
     * @param integer $type The inheritance type used by the class and its subclasses.
1772
     *
1773
     * @return string The literal string for the inheritance type.
1774
     *
1775
     * @throws \InvalidArgumentException When the inheritance type does not exist.
1776
     */
1777 1
    protected function getInheritanceTypeString($type)
1778
    {
1779 1
        if ( ! isset(static::$inheritanceTypeMap[$type])) {
1780 1
            throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type));
1781
        }
1782
1783 1
        return static::$inheritanceTypeMap[$type];
1784
    }
1785
1786
    /**
1787
     * @param integer $type The policy used for change-tracking for the mapped class.
1788
     *
1789
     * @return string The literal string for the change-tracking type.
1790
     *
1791
     * @throws \InvalidArgumentException When the change-tracking type does not exist.
1792
     */
1793 1
    protected function getChangeTrackingPolicyString($type)
1794
    {
1795 1
        if ( ! isset(static::$changeTrackingPolicyMap[$type])) {
1796 1
            throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type));
1797
        }
1798
1799 1
        return static::$changeTrackingPolicyMap[$type];
1800
    }
1801
1802
    /**
1803
     * @param integer $type The generator to use for the mapped class.
1804
     *
1805
     * @return string The literal string for the generator type.
1806
     *
1807
     * @throws \InvalidArgumentException    When the generator type does not exist.
1808
     */
1809 26
    protected function getIdGeneratorTypeString($type)
1810
    {
1811 26
        if ( ! isset(static::$generatorStrategyMap[$type])) {
1812 1
            throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type));
1813
        }
1814
1815 26
        return static::$generatorStrategyMap[$type];
1816
    }
1817
1818
    /**
1819
     * @param array $fieldMapping
1820
     *
1821
     * @return string|null
1822
     */
1823 31
    private function nullableFieldExpression(array $fieldMapping)
1824
    {
1825 31
        if (isset($fieldMapping['nullable']) && true === $fieldMapping['nullable']) {
1826 7
            return 'null';
1827
        }
1828
1829 31
        return null;
1830
    }
1831
1832
    /**
1833
     * Exports (nested) option elements.
1834
     *
1835
     * @param array $options
1836
     *
1837
     * @return string
1838
     */
1839 1
    private function exportTableOptions(array $options)
1840
    {
1841 1
        $optionsStr = [];
1842
1843 1
        foreach ($options as $name => $option) {
1844 1
            if (is_array($option)) {
1845 1
                $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}';
1846
            } else {
1847 1
                $optionsStr[] = '"' . $name . '"="' . (string) $option . '"';
1848
            }
1849
        }
1850
1851 1
        return implode(',', $optionsStr);
1852
    }
1853
}
1854