Failed Conditions
Push — develop ( ba9041...24f682 )
by Guilherme
64:30
created

testEmbeddedEntityWithNamedColumnPrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\ORM\Tools;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\DBAL\Types\Type;
10
use Doctrine\ORM\Mapping\ClassMetadataFactory;
11
use Doctrine\ORM\Mapping\ClassMetadata;
12
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
13
use Doctrine\ORM\Mapping;
14
use Doctrine\ORM\Reflection\RuntimeReflectionService;
15
use Doctrine\ORM\Reflection\StaticReflectionService;
16
use Doctrine\ORM\Tools\EntityGenerator;
17
use Doctrine\Tests\Models\DDC2372\DDC2372Admin;
18
use Doctrine\Tests\Models\DDC2372\DDC2372User;
19
use Doctrine\Tests\OrmTestCase;
20
use SebastianBergmann\Environment\Runtime;
21
22
class EntityGeneratorTest extends OrmTestCase
23
{
24
    /**
25
     * @var EntityGenerator
26
     */
27
    private $generator;
28
    private $tmpDir;
29
    private $namespace;
30
31
    /**
32
     * @var Mapping\ClassMetadataBuildingContext
33
     */
34
    private $staticMetadataBuildingContext;
35
36
    /**
37
     * @var Mapping\ClassMetadataBuildingContext
38
     */
39
    private $runtimeMetadataBuildingContext;
40
41
    public function setUp()
42
    {
43
        $this->staticMetadataBuildingContext = new Mapping\ClassMetadataBuildingContext(
44
            $this->createMock(ClassMetadataFactory::class),
45
            new StaticReflectionService()
46
        );
47
48
        $this->runtimeMetadataBuildingContext = new Mapping\ClassMetadataBuildingContext(
49
            $this->createMock(ClassMetadataFactory::class),
50
            new RuntimeReflectionService()
51
        );
52
53
        $this->namespace = uniqid("doctrine_", false);
54
        $this->tmpDir = sys_get_temp_dir();
55
56
        mkdir($this->tmpDir . \DIRECTORY_SEPARATOR . $this->namespace);
57
58
        $this->generator = new EntityGenerator();
59
60
        $this->generator->setGenerateAnnotations(true);
61
        $this->generator->setGenerateStubMethods(true);
62
        $this->generator->setRegenerateEntityIfExists(false);
63
        $this->generator->setUpdateEntityIfExists(true);
64
        $this->generator->setFieldVisibility(EntityGenerator::FIELD_VISIBLE_PROTECTED);
65
    }
66
67
    public function tearDown()
68
    {
69
        $ri = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir . '/' . $this->namespace));
70
71
        foreach ($ri AS $file) {
72
            /* @var $file \SplFileInfo */
73
            if ($file->isFile()) {
74
                unlink($file->getPathname());
75
            }
76
        }
77
78
        rmdir($this->tmpDir . '/' . $this->namespace);
79
    }
80
81
    /**
82
     * @param ClassMetadata[] $embeddedClasses
83
     *
84
     * @return ClassMetadata
85
     */
86
    public function generateBookEntityFixture(array $embeddedClasses = [])
87
    {
88
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook', $this->staticMetadataBuildingContext);
89
90
        $metadata->setCustomRepositoryClassName($this->namespace . '\EntityGeneratorBookRepository');
91
92
        $tableMetadata = new Mapping\TableMetadata();
93
94
        $tableMetadata->setName('book');
95
        $tableMetadata->addUniqueConstraint(
96
            [
97
                'name'    => 'name_uniq',
98
                'columns' => ['name'],
99
                'options' => [],
100
                'flags'   => [],
101
            ]
102
        );
103
104
        $tableMetadata->addIndex(
105
            [
106
                'name'    => 'status_idx',
107
                'columns' => ['status'],
108
                'unique'  => false,
109
                'options' => [],
110
                'flags'   => [],
111
            ]
112
        );
113
114
        $metadata->setTable($tableMetadata);
115
116
        // Property: "name"
117
        $fieldMetadata = new Mapping\FieldMetadata('name');
118
119
        $fieldMetadata->setType(Type::getType('string'));
120
        $fieldMetadata->setNullable(false);
121
        $fieldMetadata->setUnique(false);
122
123
        $metadata->addProperty($fieldMetadata);
124
125
        // Property: "status"
126
        $fieldMetadata = new Mapping\FieldMetadata('status');
127
128
        $fieldMetadata->setType(Type::getType('string'));
129
        $fieldMetadata->setNullable(false);
130
        $fieldMetadata->setUnique(false);
131
        $fieldMetadata->setOptions([
132
            'default' => 'published',
133
        ]);
134
135
        $metadata->addProperty($fieldMetadata);
136
137
        // Property: "id"
138
        $fieldMetadata = new Mapping\FieldMetadata('id');
139
140
        $fieldMetadata->setType(Type::getType('integer'));
141
        $fieldMetadata->setPrimaryKey(true);
142
        $fieldMetadata->setNullable(false);
143
        $fieldMetadata->setUnique(false);
144
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::AUTO));
145
146
        $metadata->addProperty($fieldMetadata);
147
148
        // Property: "author"
149
        $joinColumns = [];
150
151
        $joinColumn = new Mapping\JoinColumnMetadata();
152
        $joinColumn->setColumnName('author_id');
153
        $joinColumn->setReferencedColumnName('id');
154
155
        $joinColumns[] = $joinColumn;
156
157
        $association = new Mapping\OneToOneAssociationMetadata('author');
158
159
        $association->setJoinColumns($joinColumns);
160
        $association->setTargetEntity(EntityGeneratorAuthor::class);
161
        $association->setMappedBy('book');
162
163
        $metadata->addProperty($association);
164
165
        // Property: "comments"
166
        $joinTable = new Mapping\JoinTableMetadata();
167
        $joinTable->setName('book_comment');
168
169
        $joinColumn = new Mapping\JoinColumnMetadata();
170
        $joinColumn->setColumnName("book_id");
171
        $joinColumn->setReferencedColumnName("id");
172
173
        $joinTable->addJoinColumn($joinColumn);
174
175
        $joinColumn = new Mapping\JoinColumnMetadata();
176
        $joinColumn->setColumnName("comment_id");
177
        $joinColumn->setReferencedColumnName("id");
178
179
        $joinTable->addInverseJoinColumn($joinColumn);
180
181
        $association = new Mapping\ManyToManyAssociationMetadata('comments');
182
183
        $association->setJoinTable($joinTable);
184
        $association->setTargetEntity(EntityGeneratorComment::class);
185
        $association->setFetchMode(Mapping\FetchMode::EXTRA_LAZY);
186
187
        $metadata->addProperty($association);
188
189
        $metadata->addLifecycleCallback('loading', 'postLoad');
190
        $metadata->addLifecycleCallback('willBeRemoved', 'preRemove');
191
192 View Code Duplication
        foreach ($embeddedClasses as $fieldName => $embeddedClass) {
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...
193
            $metadata->mapEmbedded(
194
                [
195
                    'fieldName'    => $fieldName,
196
                    'class'        => $embeddedClass->getClassName(),
197
                    'columnPrefix' => null,
198
                ]
199
            );
200
        }
201
202
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
203
204
        return $metadata;
205
    }
206
207
    private function generateEntityTypeFixture(array $field)
208
    {
209
        $metadata = new ClassMetadata($this->namespace . '\EntityType', $this->staticMetadataBuildingContext);
210
211
        $tableMetadata = new Mapping\TableMetadata();
212
        $tableMetadata->setName('entity_type');
213
214
        $metadata->setTable($tableMetadata);
215
216
        $fieldMetadata = new Mapping\FieldMetadata('id');
217
        $fieldMetadata->setType(Type::getType('integer'));
218
        $fieldMetadata->setPrimaryKey(true);
219
        $fieldMetadata->setNullable(false);
220
        $fieldMetadata->setUnique(false);
221
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::AUTO));
222
223
        $metadata->addProperty($fieldMetadata);
224
225
        $fieldMetadata = new Mapping\FieldMetadata($field['fieldName']);
226
        $fieldMetadata->setType(Type::getType($field['dbType']));
227
        $fieldMetadata->setNullable(false);
228
        $fieldMetadata->setUnique(false);
229
230
        $metadata->addProperty($fieldMetadata);
231
232
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
233
234
        return $metadata;
235
    }
236
237
    /**
238
     * @return ClassMetadata
239
     */
240
    private function generateIsbnEmbeddableFixture(array $embeddedClasses = [], $columnPrefix = null)
241
    {
242
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorIsbn', $this->staticMetadataBuildingContext);
243
        $metadata->isEmbeddedClass = true;
244
245
        $fieldMetadata = new Mapping\FieldMetadata('prefix');
246
        $fieldMetadata->setType(Type::getType('integer'));
247
        $fieldMetadata->setNullable(false);
248
        $fieldMetadata->setUnique(false);
249
250
        $metadata->addProperty($fieldMetadata);
251
252
        $fieldMetadata = new Mapping\FieldMetadata('groupNumber');
253
        $fieldMetadata->setType(Type::getType('integer'));
254
        $fieldMetadata->setNullable(false);
255
        $fieldMetadata->setUnique(false);
256
257
        $metadata->addProperty($fieldMetadata);
258
259
        $fieldMetadata = new Mapping\FieldMetadata('publisherNumber');
260
        $fieldMetadata->setType(Type::getType('integer'));
261
        $fieldMetadata->setNullable(false);
262
        $fieldMetadata->setUnique(false);
263
264
        $metadata->addProperty($fieldMetadata);
265
266
        $fieldMetadata = new Mapping\FieldMetadata('titleNumber');
267
        $fieldMetadata->setType(Type::getType('integer'));
268
        $fieldMetadata->setNullable(false);
269
        $fieldMetadata->setUnique(false);
270
271
        $metadata->addProperty($fieldMetadata);
272
273
        $fieldMetadata = new Mapping\FieldMetadata('checkDigit');
274
        $fieldMetadata->setType(Type::getType('integer'));
275
        $fieldMetadata->setNullable(false);
276
        $fieldMetadata->setUnique(false);
277
278
        $metadata->addProperty($fieldMetadata);
279
280 View Code Duplication
        foreach ($embeddedClasses as $fieldName => $embeddedClass) {
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...
281
            $metadata->mapEmbedded(
282
                [
283
                    'fieldName'    => $fieldName,
284
                    'class'        => $embeddedClass->getClassName(),
285
                    'columnPrefix' => $columnPrefix,
286
                ]
287
            );
288
        }
289
290
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
291
292
        return $metadata;
293
    }
294
295
    /**
296
     * @return ClassMetadata
297
     */
298
    private function generateTestEmbeddableFixture()
299
    {
300
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorTestEmbeddable', $this->staticMetadataBuildingContext);
301
302
        $metadata->isEmbeddedClass = true;
303
304
        $fieldMetadata = new Mapping\FieldMetadata('field1');
305
        $fieldMetadata->setType(Type::getType('integer'));
306
        $fieldMetadata->setNullable(false);
307
        $fieldMetadata->setUnique(false);
308
309
        $metadata->addProperty($fieldMetadata);
310
311
        $fieldMetadata = new Mapping\FieldMetadata('field2');
312
        $fieldMetadata->setType(Type::getType('integer'));
313
        $fieldMetadata->setNullable(true);
314
        $fieldMetadata->setUnique(false);
315
316
        $metadata->addProperty($fieldMetadata);
317
318
        $fieldMetadata = new Mapping\FieldMetadata('field3');
319
        $fieldMetadata->setType(Type::getType('datetime'));
320
        $fieldMetadata->setNullable(false);
321
        $fieldMetadata->setUnique(false);
322
323
        $metadata->addProperty($fieldMetadata);
324
325
        $fieldMetadata = new Mapping\FieldMetadata('field4');
326
        $fieldMetadata->setType(Type::getType('datetime'));
327
        $fieldMetadata->setNullable(true);
328
        $fieldMetadata->setUnique(false);
329
330
        $metadata->addProperty($fieldMetadata);
331
332
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
333
334
        return $metadata;
335
    }
336
337
    /**
338
     * @param ClassMetadata $metadata
339
     */
340
    private function loadEntityClass(ClassMetadata $metadata)
341
    {
342
        $className = basename(str_replace('\\', '/', $metadata->getClassName()));
343
        $path      = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php';
344
345
        self::assertFileExists($path);
346
347
        require_once $path;
348
    }
349
350
    /**
351
     * @param  ClassMetadata $metadata
352
     *
353
     * @return mixed An instance of the given metadata's class.
354
     */
355
    public function newInstance(ClassMetadata $metadata)
356
    {
357
        $this->loadEntityClass($metadata);
358
359
        $className = $metadata->getClassName();
360
361
        return new $className;
362
    }
363
364
    /**
365
     * @group embedded
366
     * @group GH-6314
367
     */
368
    public function testEmbeddedEntityWithNamedColumnPrefix()
369
    {
370
        $columnPrefix = 'GH6314Prefix_';
371
        $testMetadata = $this->generateTestEmbeddableFixture();
372
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], $columnPrefix);
373
        $isbnEntity = $this->newInstance($isbnMetadata);
374
        $refClass = new \ReflectionClass($isbnEntity);
375
        self::assertTrue($refClass->hasProperty('testEmbedded'));
376
377
        $docComment = $refClass->getProperty('testEmbedded')->getDocComment();
378
        $needle = sprintf('@Embedded(class="%s", columnPrefix="%s")', $testMetadata->getClassName(), $columnPrefix);
379
        self::assertContains($needle, $docComment);
380
    }
381
382
    /**
383
     * @group embedded
384
     * @group GH-6314
385
     */
386
    public function testEmbeddedEntityWithoutColumnPrefix()
387
    {
388
        $testMetadata = $this->generateTestEmbeddableFixture();
389
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], false);
390
        $isbnEntity = $this->newInstance($isbnMetadata);
391
        $refClass = new \ReflectionClass($isbnEntity);
392
        self::assertTrue($refClass->hasProperty('testEmbedded'));
393
394
        $docComment = $refClass->getProperty('testEmbedded')->getDocComment();
395
        $needle = sprintf('@Embedded(class="%s", columnPrefix=false)', $testMetadata->getClassName());
396
        self::assertContains($needle, $docComment);
397
    }
398
399
    /**
400
     * @group embedded
401
     */
402
    public function testGeneratedEntityClass()
403
    {
404
        $testMetadata = $this->generateTestEmbeddableFixture();
405
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['test' => $testMetadata]);
406
        $metadata     = $this->generateBookEntityFixture(['isbn' => $isbnMetadata]);
407
        $book         = $this->newInstance($metadata);
408
409
        $bookClassName = $metadata->getClassName();
410
411
        self::assertTrue(class_exists($bookClassName), "Class does not exist.");
412
        self::assertTrue(method_exists($bookClassName, '__construct'), "EntityGeneratorBook::__construct() missing.");
413
        self::assertTrue(method_exists($bookClassName, 'getId'), "EntityGeneratorBook::getId() missing.");
414
        self::assertTrue(method_exists($bookClassName, 'setName'), "EntityGeneratorBook::setName() missing.");
415
        self::assertTrue(method_exists($bookClassName, 'getName'), "EntityGeneratorBook::getName() missing.");
416
        self::assertTrue(method_exists($bookClassName, 'setStatus'), "EntityGeneratorBook::setStatus() missing.");
417
        self::assertTrue(method_exists($bookClassName, 'getStatus'), "EntityGeneratorBook::getStatus() missing.");
418
        self::assertTrue(method_exists($bookClassName, 'setAuthor'), "EntityGeneratorBook::setAuthor() missing.");
419
        self::assertTrue(method_exists($bookClassName, 'getAuthor'), "EntityGeneratorBook::getAuthor() missing.");
420
        self::assertTrue(method_exists($bookClassName, 'getComments'), "EntityGeneratorBook::getComments() missing.");
421
        self::assertTrue(method_exists($bookClassName, 'addComment'), "EntityGeneratorBook::addComment() missing.");
422
        self::assertTrue(method_exists($bookClassName, 'removeComment'), "EntityGeneratorBook::removeComment() missing.");
423
        self::assertTrue(method_exists($bookClassName, 'setIsbn'), "EntityGeneratorBook::setIsbn() missing.");
424
        self::assertTrue(method_exists($bookClassName, 'getIsbn'), "EntityGeneratorBook::getIsbn() missing.");
425
426
        $reflClass = new \ReflectionClass($metadata->getClassName());
427
428
        self::assertCount(6, $reflClass->getProperties());
429
        self::assertCount(15, $reflClass->getMethods());
430
431
        self::assertEquals('published', $book->getStatus());
432
433
        $book->setName('Jonathan H. Wage');
434
        self::assertEquals('Jonathan H. Wage', $book->getName());
435
436
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'addComment');
437
        $addCommentParameters = $reflMethod->getParameters();
438
        self::assertEquals('comment', $addCommentParameters[0]->getName());
0 ignored issues
show
Bug introduced by
Consider using $addCommentParameters[0]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
439
440
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'removeComment');
441
        $removeCommentParameters = $reflMethod->getParameters();
442
        self::assertEquals('comment', $removeCommentParameters[0]->getName());
0 ignored issues
show
Bug introduced by
Consider using $removeCommentParameters[0]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
443
444
        $author = new EntityGeneratorAuthor();
445
        $book->setAuthor($author);
446
        self::assertEquals($author, $book->getAuthor());
447
448
        $comment = new EntityGeneratorComment();
449
        self::assertInstanceOf($metadata->getClassName(), $book->addComment($comment));
450
        self::assertInstanceOf(ArrayCollection::class, $book->getComments());
451
        self::assertEquals(new ArrayCollection([$comment]), $book->getComments());
452
        self::assertInternalType('boolean', $book->removeComment($comment));
453
        self::assertEquals(new ArrayCollection([]), $book->getComments());
454
455
        $this->newInstance($isbnMetadata);
456
457
        $isbnClassName = $isbnMetadata->getClassName();
458
459
        $isbn = new $isbnClassName();
460
461
        $book->setIsbn($isbn);
462
        self::assertSame($isbn, $book->getIsbn());
463
464
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'setIsbn');
465
        $reflParameters = $reflMethod->getParameters();
466
        self::assertEquals($isbnMetadata->getClassName(), $reflParameters[0]->getClass()->name);
467
    }
468
469
    /**
470
     * @group embedded
471
     */
472
    public function testEntityUpdatingWorks()
473
    {
474
        $metadata = $this->generateBookEntityFixture(['isbn' => $this->generateIsbnEmbeddableFixture()]);
475
476
        $fieldMetadata = new Mapping\FieldMetadata('test');
477
478
        $fieldMetadata->setType(Type::getType('string'));
479
480
        $metadata->addProperty($fieldMetadata);
481
482
        $testEmbeddableMetadata = $this->generateTestEmbeddableFixture();
483
484
        $metadata->mapEmbedded(
485
            [
486
            'fieldName'    => 'testEmbedded',
487
            'class'        => $testEmbeddableMetadata->getClassName(),
488
            'columnPrefix' => null,
489
            ]
490
        );
491
492
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
493
494
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/EntityGeneratorBook.php~");
495
496
        $book = $this->newInstance($metadata);
0 ignored issues
show
Unused Code introduced by
$book is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
497
        $reflClass = new \ReflectionClass($metadata->getClassName());
498
499
        self::assertTrue($reflClass->hasProperty('name'), "Regenerating keeps property 'name'.");
500
        self::assertTrue($reflClass->hasProperty('status'), "Regenerating keeps property 'status'.");
501
        self::assertTrue($reflClass->hasProperty('id'), "Regenerating keeps property 'id'.");
502
        self::assertTrue($reflClass->hasProperty('isbn'), "Regenerating keeps property 'isbn'.");
503
504
        self::assertTrue($reflClass->hasProperty('test'), "Check for property test failed.");
505
        self::assertTrue($reflClass->getProperty('test')->isProtected(), "Check for protected property test failed.");
506
        self::assertTrue($reflClass->hasProperty('testEmbedded'), "Check for property testEmbedded failed.");
507
        self::assertTrue($reflClass->getProperty('testEmbedded')->isProtected(), "Check for protected property testEmbedded failed.");
508
        self::assertTrue($reflClass->hasMethod('getTest'), "Check for method 'getTest' failed.");
509
        self::assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed.");
510
        self::assertTrue($reflClass->hasMethod('setTest'), "Check for method 'setTest' failed.");
511
        self::assertTrue($reflClass->getMethod('setTest')->isPublic(), "Check for public visibility of method 'setTest' failed.");
512
        self::assertTrue($reflClass->hasMethod('getTestEmbedded'), "Check for method 'getTestEmbedded' failed.");
513
        self::assertTrue(
514
            $reflClass->getMethod('getTestEmbedded')->isPublic(),
515
            "Check for public visibility of method 'getTestEmbedded' failed."
516
        );
517
        self::assertTrue($reflClass->hasMethod('setTestEmbedded'), "Check for method 'setTestEmbedded' failed.");
518
        self::assertTrue(
519
            $reflClass->getMethod('setTestEmbedded')->isPublic(),
520
            "Check for public visibility of method 'setTestEmbedded' failed."
521
        );
522
    }
523
524
    /**
525
     * @group DDC-2121
526
     */
527
    public function testMethodDocBlockShouldStartWithBackSlash()
528
    {
529
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
530
        $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
531
        $book     = $this->newInstance($metadata);
532
533
        self::assertPhpDocVarType('\Doctrine\Common\Collections\Collection', new \ReflectionProperty($book, 'comments'));
534
        self::assertPhpDocReturnType('\Doctrine\Common\Collections\Collection', new \ReflectionMethod($book, 'getComments'));
535
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'addComment'));
536
        self::assertPhpDocReturnType('EntityGeneratorBook', new \ReflectionMethod($book, 'addComment'));
537
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'removeComment'));
538
        self::assertPhpDocReturnType('boolean', new \ReflectionMethod($book, 'removeComment'));
539
540
        self::assertPhpDocVarType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new \ReflectionProperty($book, 'author'));
541
        self::assertPhpDocReturnType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new \ReflectionMethod($book, 'getAuthor'));
542
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new \ReflectionMethod($book, 'setAuthor'));
543
544
//        $expectedClassName = '\\' . $embeddedMetadata->getClassName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
545
//        self::assertPhpDocVarType($expectedClassName, new \ReflectionProperty($book, 'isbn'));
546
//        self::assertPhpDocReturnType($expectedClassName, new \ReflectionMethod($book, 'getIsbn'));
547
//        self::assertPhpDocParamType($expectedClassName, new \ReflectionMethod($book, 'setIsbn'));
548
    }
549
550
    public function testEntityExtendsStdClass()
551
    {
552
        $this->generator->setClassToExtend('stdClass');
553
        $metadata = $this->generateBookEntityFixture();
554
555
        $book = $this->newInstance($metadata);
556
        self::assertInstanceOf('stdClass', $book);
557
558
        $metadata = $this->generateIsbnEmbeddableFixture();
559
        $isbn = $this->newInstance($metadata);
560
        self::assertInstanceOf('stdClass', $isbn);
561
    }
562
563
    public function testLifecycleCallbacks()
564
    {
565
        $metadata = $this->generateBookEntityFixture();
566
567
        $book = $this->newInstance($metadata);
0 ignored issues
show
Unused Code introduced by
$book is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
568
        $reflClass = new \ReflectionClass($metadata->getClassName());
569
570
        self::assertTrue($reflClass->hasMethod('loading'), "Check for postLoad lifecycle callback.");
571
        self::assertTrue($reflClass->hasMethod('willBeRemoved'), "Check for preRemove lifecycle callback.");
572
    }
573
574
    public function testLoadMetadata()
575
    {
576
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
577
        $metadata         = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
578
        $book             = $this->newInstance($metadata);
0 ignored issues
show
Unused Code introduced by
$book is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
579
580
        $cm = new ClassMetadata($metadata->getClassName(), $this->runtimeMetadataBuildingContext);
581
582
        $driver = $this->createAnnotationDriver();
583
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->runtimeMetadataBuildingContext);
584
585
        self::assertEquals($cm->getTableName(), $metadata->getTableName());
586
        self::assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
587
        self::assertEquals($cm->identifier, $metadata->identifier);
588
        self::assertEquals($cm->getCustomRepositoryClassName(), $metadata->getCustomRepositoryClassName());
589
//        self::assertEquals($cm->embeddedClasses, $metadata->embeddedClasses);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
590
//        self::assertEquals($cm->isEmbeddedClass, $metadata->isEmbeddedClass);
591
592
        self::assertEquals(Mapping\FetchMode::EXTRA_LAZY, $cm->getProperty('comments')->getFetchMode());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\ORM\Mapping\Property as the method getFetchMode() does only exist in the following implementations of said interface: Doctrine\ORM\Mapping\AssociationMetadata, Doctrine\ORM\Mapping\ManyToManyAssociationMetadata, Doctrine\ORM\Mapping\ManyToOneAssociationMetadata, Doctrine\ORM\Mapping\OneToManyAssociationMetadata, Doctrine\ORM\Mapping\OneToOneAssociationMetadata, Doctrine\ORM\Mapping\ToManyAssociationMetadata, Doctrine\ORM\Mapping\ToOneAssociationMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
593
594
//        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
595
//
596
//        $cm = new ClassMetadata($embeddedMetadata->getClassName(), $this->runtimeMetadataBuildingContext);
597
//
598
//        $driver->loadMetadataForClass($cm->getClassName(), $cm);
599
//
600
//        self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
601
//        self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
602
    }
603
604
    public function testLoadPrefixedMetadata()
605
    {
606
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
607
        $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
608
609
        $reader = new AnnotationReader();
610
        $driver = new AnnotationDriver($reader, []);
611
612
        $book = $this->newInstance($metadata);
0 ignored issues
show
Unused Code introduced by
$book is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
613
614
        $cm = new ClassMetadata($metadata->getClassName(), $this->runtimeMetadataBuildingContext);
615
616
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->runtimeMetadataBuildingContext);
617
618
        self::assertEquals($cm->getTableName(), $metadata->getTableName());
619
        self::assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
620
        self::assertEquals($cm->identifier, $metadata->identifier);
621
        self::assertEquals($cm->getCustomRepositoryClassName(), $metadata->getCustomRepositoryClassName());
622
623
//        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
624
//
625
//        $cm = new ClassMetadata($embeddedMetadata->getClassName(), $this->runtimeMetadataBuildingContext);
626
//
627
//        $driver->loadMetadataForClass($cm->getClassName(), $cm);
628
//
629
//        self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
630
//        self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
631
    }
632
633
    /**
634
     * @group DDC-3272
635
     */
636
    public function testMappedSuperclassAnnotationGeneration()
637
    {
638
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook', $this->staticMetadataBuildingContext);
639
640
        $metadata->isMappedSuperclass = true;
641
642
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
643
644
        $this->newInstance($metadata); // force instantiation (causes autoloading to kick in)
645
646
        $driver = new AnnotationDriver(new AnnotationReader(), []);
647
        $cm     = new ClassMetadata($metadata->getClassName(), $this->runtimeMetadataBuildingContext);
648
649
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->runtimeMetadataBuildingContext);
650
651
        self::assertTrue($cm->isMappedSuperclass);
652
    }
653
654
    /**
655
     * @dataProvider getParseTokensInEntityFileData
656
     */
657
    public function testParseTokensInEntityFile($php, $classes)
658
    {
659
        $r = new \ReflectionObject($this->generator);
660
        $m = $r->getMethod('parseTokensInEntityFile');
661
        $m->setAccessible(true);
662
663
        $p = $r->getProperty('staticReflection');
664
        $p->setAccessible(true);
665
666
        $ret = $m->invoke($this->generator, $php);
0 ignored issues
show
Unused Code introduced by
$ret is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
667
        self::assertEquals($classes, array_keys($p->getValue($this->generator)));
668
    }
669
670
    /**
671
     * @group DDC-1784
672
     */
673
    public function testGenerateEntityWithSequenceGenerator()
674
    {
675
        $metadata = new ClassMetadata($this->namespace . '\DDC1784Entity', $this->staticMetadataBuildingContext);
676
677
        $fieldMetadata = new Mapping\FieldMetadata('id');
678
        $fieldMetadata->setType(Type::getType('integer'));
679
        $fieldMetadata->setPrimaryKey(true);
680
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(
681
            Mapping\GeneratorType::SEQUENCE,
682
            [
683
                'sequenceName'   => 'DDC1784_ID_SEQ',
684
                'allocationSize' => 1,
685
            ]
686
        ));
687
688
        $metadata->addProperty($fieldMetadata);
689
690
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
691
692
        $filename = $this->tmpDir . DIRECTORY_SEPARATOR
693
                  . $this->namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php';
694
695
        self::assertFileExists($filename);
696
697
        require_once $filename;
698
699
        $reflection = new \ReflectionProperty($metadata->getClassName(), 'id');
700
        $docComment = $reflection->getDocComment();
701
702
        self::assertContains('@ORM\Id', $docComment);
703
        self::assertContains('@ORM\Column(name="id", type="integer")', $docComment);
704
        self::assertContains('@ORM\GeneratedValue(strategy="SEQUENCE")', $docComment);
705
        self::assertContains('@ORM\SequenceGenerator(sequenceName="DDC1784_ID_SEQ", allocationSize=1)', $docComment);
706
    }
707
708
    /**
709
     * @group DDC-2079
710
     */
711
    public function testGenerateEntityWithMultipleInverseJoinColumns()
712
    {
713
        $metadata = new ClassMetadata($this->namespace . '\DDC2079Entity', $this->staticMetadataBuildingContext);
714
715
        $fieldMetadata = new Mapping\FieldMetadata('id');
716
        $fieldMetadata->setType(Type::getType('integer'));
717
        $fieldMetadata->setPrimaryKey(true);
718
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::SEQUENCE));
719
720
        $metadata->addProperty($fieldMetadata);
721
722
        $joinTable = new Mapping\JoinTableMetadata();
723
        $joinTable->setName('unidade_centro_custo');
724
725
        $joinColumn = new Mapping\JoinColumnMetadata();
726
        $joinColumn->setColumnName("idorcamento");
727
        $joinColumn->setReferencedColumnName("idorcamento");
728
729
        $joinTable->addJoinColumn($joinColumn);
730
731
        $joinColumn = new Mapping\JoinColumnMetadata();
732
        $joinColumn->setColumnName("idunidade");
733
        $joinColumn->setReferencedColumnName("idunidade");
734
735
        $joinTable->addJoinColumn($joinColumn);
736
737
        $joinColumn = new Mapping\JoinColumnMetadata();
738
        $joinColumn->setColumnName("idcentrocusto");
739
        $joinColumn->setReferencedColumnName("idcentrocusto");
740
741
        $joinTable->addInverseJoinColumn($joinColumn);
742
743
        $joinColumn = new Mapping\JoinColumnMetadata();
744
        $joinColumn->setColumnName("idpais");
745
        $joinColumn->setReferencedColumnName("idpais");
746
747
        $joinTable->addInverseJoinColumn($joinColumn);
748
749
        $association = new Mapping\ManyToManyAssociationMetadata('centroCustos');
750
751
        $association->setJoinTable($joinTable);
752
        $association->setTargetEntity($this->namespace . '\\DDC2079CentroCusto');
753
754
        $metadata->addProperty($association);
755
756
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
757
758
        $filename = $this->tmpDir . DIRECTORY_SEPARATOR
759
            . $this->namespace . DIRECTORY_SEPARATOR . 'DDC2079Entity.php';
760
761
        self::assertFileExists($filename);
762
763
        require_once $filename;
764
765
        $property   = new \ReflectionProperty($metadata->getClassName(), 'centroCustos');
766
        $docComment = $property->getDocComment();
767
768
        //joinColumns
769
        self::assertContains('@ORM\JoinColumn(name="idorcamento", referencedColumnName="idorcamento"),', $docComment);
770
        self::assertContains('@ORM\JoinColumn(name="idunidade", referencedColumnName="idunidade")', $docComment);
771
        //inverseJoinColumns
772
        self::assertContains('@ORM\JoinColumn(name="idcentrocusto", referencedColumnName="idcentrocusto"),', $docComment);
773
        self::assertContains('@ORM\JoinColumn(name="idpais", referencedColumnName="idpais")', $docComment);
774
775
    }
776
777
     /**
778
     * @group DDC-2172
779
     */
780 View Code Duplication
    public function testGetInheritanceTypeString()
781
    {
782
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
783
        $method     = new \ReflectionMethod($this->generator, 'getInheritanceTypeString');
784
        $constants  = $reflection->getConstants();
785
        $pattern    = '/^InheritanceType::/';
786
787
        $method->setAccessible(true);
788
789
        foreach ($constants as $name => $value) {
790
            if( ! preg_match($pattern, $name)) {
791
                continue;
792
            }
793
794
            $expected = preg_replace($pattern, '', $name);
795
            $actual   = $method->invoke($this->generator, $value);
796
797
            self::assertEquals($expected, $actual);
798
        }
799
800
        $this->expectException(\InvalidArgumentException::class);
801
        $this->expectExceptionMessage('Invalid provided InheritanceType: INVALID');
802
803
        $method->invoke($this->generator, 'INVALID');
804
    }
805
806
    /**
807
    * @group DDC-2172
808
    */
809 View Code Duplication
    public function testGetChangeTrackingPolicyString()
810
    {
811
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
812
        $method     = new \ReflectionMethod($this->generator, 'getChangeTrackingPolicyString');
813
        $constants  = $reflection->getConstants();
814
        $pattern    = '/^ChangeTrackingPolicy::/';
815
816
        $method->setAccessible(true);
817
818
        foreach ($constants as $name => $value) {
819
            if( ! preg_match($pattern, $name)) {
820
                continue;
821
            }
822
823
            $expected = preg_replace($pattern, '', $name);
824
            $actual   = $method->invoke($this->generator, $value);
825
826
            self::assertEquals($expected, $actual);
827
        }
828
829
        $this->expectException(\InvalidArgumentException::class);
830
        $this->expectExceptionMessage('Invalid provided ChangeTrackingPolicy: INVALID');
831
832
        $method->invoke($this->generator, 'INVALID');
833
    }
834
835
    /**
836
     * @group DDC-2172
837
     */
838 View Code Duplication
    public function testGetIdGeneratorTypeString()
839
    {
840
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
841
        $method     = new \ReflectionMethod($this->generator, 'getIdGeneratorTypeString');
842
        $constants  = $reflection->getConstants();
843
        $pattern    = '/^GeneratorType::/';
844
845
        $method->setAccessible(true);
846
847
        foreach ($constants as $name => $value) {
848
            if( ! preg_match($pattern, $name)) {
849
                continue;
850
            }
851
852
            $expected = preg_replace($pattern, '', $name);
853
            $actual   = $method->invoke($this->generator, $value);
854
855
            self::assertEquals($expected, $actual);
856
        }
857
858
        $this->expectException(\InvalidArgumentException::class);
859
        $this->expectExceptionMessage('Invalid provided IdGeneratorType: INVALID');
860
861
        $method->invoke($this->generator, 'INVALID');
862
    }
863
864
    /**
865
     * @dataProvider getEntityTypeAliasDataProvider
866
     *
867
     * @group DDC-1694
868
     */
869
    public function testEntityTypeAlias(array $field)
870
    {
871
        $metadata   = $this->generateEntityTypeFixture($field);
872
        $path       = $this->tmpDir . '/'. $this->namespace . '/EntityType.php';
873
874
        self::assertFileExists($path);
875
        require_once $path;
876
877
        $entityClassName = $metadata->getClassName();
878
879
        $entity     = new $entityClassName;
880
        $reflClass  = new \ReflectionClass($entityClassName);
881
882
        $type   = $field['phpType'];
883
        $name   = $field['fieldName'];
884
        $value  = $field['value'];
885
        $getter = "get" . ucfirst($name);
886
        $setter = "set" . ucfirst($name);
887
888
        self::assertPhpDocVarType($type, $reflClass->getProperty($name));
889
        self::assertPhpDocParamType($type, $reflClass->getMethod($setter));
890
        self::assertPhpDocReturnType($type, $reflClass->getMethod($getter));
891
892
        self::assertSame($entity, $entity->{$setter}($value));
893
        self::assertEquals($value, $entity->{$getter}());
894
    }
895
896
    /**
897
     * @group DDC-2372
898
     */
899 View Code Duplication
    public function testTraitPropertiesAndMethodsAreNotDuplicated()
900
    {
901
        $cmf = new ClassMetadataFactory();
902
        $em = $this->getTestEntityManager();
903
        $cmf->setEntityManager($em);
904
905
        $user = new DDC2372User();
906
        $metadata = $cmf->getMetadataFor(get_class($user));
907
908
        // @todo guilhermeblanco Fix this test as changing Entity class should never be allowed.
909
        $metadata->setClassName($this->namespace . "\DDC2372User");
910
911
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
912
913
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/DDC2372User.php");
914
915
        require $this->tmpDir . "/" . $this->namespace . "/DDC2372User.php";
916
917
        $reflClass = new \ReflectionClass($metadata->getClassName());
918
919
        self::assertSame($reflClass->hasProperty('address'), false);
920
        self::assertSame($reflClass->hasMethod('setAddress'), false);
921
        self::assertSame($reflClass->hasMethod('getAddress'), false);
922
    }
923
924
    /**
925
     * @group DDC-2372
926
     */
927 View Code Duplication
    public function testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses()
928
    {
929
        $cmf = new ClassMetadataFactory();
930
        $em = $this->getTestEntityManager();
931
        $cmf->setEntityManager($em);
932
933
        $user = new DDC2372Admin();
934
        $metadata = $cmf->getMetadataFor(get_class($user));
935
936
        // @todo guilhermeblanco Fix this test as changing Entity class should never be allowed.
937
        $metadata->setClassName($this->namespace . "\DDC2372Admin");
938
939
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
940
941
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/DDC2372Admin.php");
942
        require $this->tmpDir . "/" . $this->namespace . "/DDC2372Admin.php";
943
944
        $reflClass = new \ReflectionClass($metadata->getClassName());
945
946
        self::assertSame($reflClass->hasProperty('address'), false);
947
        self::assertSame($reflClass->hasMethod('setAddress'), false);
948
        self::assertSame($reflClass->hasMethod('getAddress'), false);
949
    }
950
951
    /**
952
     * @group DDC-1590
953
     */
954
    public function testMethodsAndPropertiesAreNotDuplicatedInChildClasses()
955
    {
956
        $cmf    = new ClassMetadataFactory();
957
        $em     = $this->getTestEntityManager();
958
959
        $cmf->setEntityManager($em);
960
961
        $ns     = $this->namespace;
962
        $nsdir  = $this->tmpDir . '/' . $ns;
963
964
        // Dump DDC1590User into temp file
965
        $content = str_replace(
966
            'namespace Doctrine\Tests\Models\DDC1590',
967
            'namespace ' . $ns,
968
            file_get_contents(__DIR__ . '/../../Models/DDC1590/DDC1590User.php')
969
        );
970
971
        $fname = $nsdir . "/DDC1590User.php";
972
        file_put_contents($fname, $content);
973
974
        // Require DDC1590User
975
        require $fname;
976
977
        $metadata = $cmf->getMetadataFor($ns . '\DDC1590User');
978
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
979
980
        // class DDC1590User extends DDC1590Entity { ... }
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
981
        $source = file_get_contents($fname);
982
983
        // class _DDC1590User extends DDC1590Entity { ... }
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
984
        $source2    = str_replace('class DDC1590User', 'class _DDC1590User', $source);
985
        $fname2     = $nsdir . "/_DDC1590User.php";
986
        file_put_contents($fname2, $source2);
987
        require $fname2;
988
989
        // class __DDC1590User { ... }
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
990
        $source3    = str_replace('class DDC1590User extends DDC1590Entity', 'class __DDC1590User', $source);
991
        $fname3     = $nsdir . "/__DDC1590User.php";
992
        file_put_contents($fname3, $source3);
993
        require $fname3;
994
995
996
        // class _DDC1590User extends DDC1590Entity { ... }
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
997
        $rc2 = new \ReflectionClass($ns.'\_DDC1590User');
998
999
        self::assertTrue($rc2->hasProperty('name'));
1000
        self::assertTrue($rc2->hasProperty('id'));
1001
        self::assertTrue($rc2->hasProperty('created_at'));
1002
1003
        self::assertTrue($rc2->hasMethod('getName'));
1004
        self::assertTrue($rc2->hasMethod('setName'));
1005
        self::assertTrue($rc2->hasMethod('getId'));
1006
        self::assertFalse($rc2->hasMethod('setId'));
1007
        self::assertTrue($rc2->hasMethod('getCreatedAt'));
1008
        self::assertTrue($rc2->hasMethod('setCreatedAt'));
1009
1010
        // class __DDC1590User { ... }
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1011
        $rc3 = new \ReflectionClass($ns.'\__DDC1590User');
1012
1013
        self::assertTrue($rc3->hasProperty('name'));
1014
        self::assertFalse($rc3->hasProperty('id'));
1015
        self::assertFalse($rc3->hasProperty('created_at'));
1016
1017
        self::assertTrue($rc3->hasMethod('getName'));
1018
        self::assertTrue($rc3->hasMethod('setName'));
1019
        self::assertFalse($rc3->hasMethod('getId'));
1020
        self::assertFalse($rc3->hasMethod('setId'));
1021
        self::assertFalse($rc3->hasMethod('getCreatedAt'));
1022
        self::assertFalse($rc3->hasMethod('setCreatedAt'));
1023
    }
1024
1025
    /**
1026
     * @group embedded
1027
     * @group DDC-3304
1028
     */
1029
    public function testGeneratedMutableEmbeddablesClass()
1030
    {
1031
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
1032
        $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]);
1033
1034
        $isbn = $this->newInstance($metadata);
1035
        $isbnClassName = $metadata->getClassName();
1036
1037
        self::assertTrue(class_exists($isbnClassName), "Class does not exist.");
1038
        self::assertFalse(method_exists($isbnClassName, '__construct'), "EntityGeneratorIsbn::__construct present.");
1039
        self::assertTrue(method_exists($isbnClassName, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
1040
        self::assertTrue(method_exists($isbnClassName, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() missing.");
1041
        self::assertTrue(method_exists($isbnClassName, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
1042
        self::assertTrue(method_exists($isbnClassName, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() missing.");
1043
        self::assertTrue(method_exists($isbnClassName, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
1044
        self::assertTrue(method_exists($isbnClassName, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() missing.");
1045
        self::assertTrue(method_exists($isbnClassName, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
1046
        self::assertTrue(method_exists($isbnClassName, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() missing.");
1047
        self::assertTrue(method_exists($isbnClassName, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
1048
        self::assertTrue(method_exists($isbnClassName, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() missing.");
1049
        self::assertTrue(method_exists($isbnClassName, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
1050
        self::assertTrue(method_exists($isbnClassName, 'setTest'), "EntityGeneratorIsbn::setTest() missing.");
1051
1052
        $isbn->setPrefix(978);
1053
        self::assertSame(978, $isbn->getPrefix());
1054
1055
        $this->newInstance($embeddedMetadata);
1056
1057
        $testEmbeddedCLassName = $embeddedMetadata->getClassName();
1058
1059
        $test = new $testEmbeddedCLassName();
1060
1061
        $isbn->setTest($test);
1062
        self::assertSame($test, $isbn->getTest());
1063
1064
        $reflMethod = new \ReflectionMethod($isbnClassName, 'setTest');
1065
        $reflParameters = $reflMethod->getParameters();
1066
        self::assertEquals($embeddedMetadata->getClassName(), $reflParameters[0]->getClass()->name);
1067
    }
1068
1069
    /**
1070
     * @group embedded
1071
     * @group DDC-3304
1072
     */
1073
    public function testGeneratedImmutableEmbeddablesClass()
1074
    {
1075
        $this->generator->setEmbeddablesImmutable(true);
1076
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
1077
        $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]);
1078
1079
        $this->loadEntityClass($embeddedMetadata);
1080
        $this->loadEntityClass($metadata);
1081
1082
        $isbnClassName = $metadata->getClassName();
1083
1084
        self::assertTrue(class_exists($isbnClassName), "Class does not exist.");
1085
        self::assertTrue(method_exists($isbnClassName, '__construct'), "EntityGeneratorIsbn::__construct missing.");
1086
        self::assertTrue(method_exists($isbnClassName, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
1087
        self::assertFalse(method_exists($isbnClassName, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() present.");
1088
        self::assertTrue(method_exists($isbnClassName, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
1089
        self::assertFalse(method_exists($isbnClassName, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() present.");
1090
        self::assertTrue(method_exists($isbnClassName, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
1091
        self::assertFalse(method_exists($isbnClassName, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() present.");
1092
        self::assertTrue(method_exists($isbnClassName, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
1093
        self::assertFalse(method_exists($isbnClassName, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() present.");
1094
        self::assertTrue(method_exists($isbnClassName, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
1095
        self::assertFalse(method_exists($isbnClassName, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() present.");
1096
        self::assertTrue(method_exists($isbnClassName, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
1097
        self::assertFalse(method_exists($isbnClassName, 'setTest'), "EntityGeneratorIsbn::setTest() present.");
1098
1099
        $embeddedClassName = $embeddedMetadata->getClassName();
1100
1101
        $test = new $embeddedClassName(1, new \DateTime());
1102
        $isbn = new $isbnClassName($test, 978, 3, 12, 732320, 83);
1103
1104
        $reflMethod = new \ReflectionMethod($isbn, '__construct');
1105
        $reflParameters = $reflMethod->getParameters();
1106
1107
        self::assertCount(6, $reflParameters);
1108
1109
        self::assertSame($embeddedMetadata->getClassName(), $reflParameters[0]->getClass()->name);
1110
        self::assertSame('test', $reflParameters[0]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[0]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1111
        self::assertFalse($reflParameters[0]->isOptional());
1112
1113
        self::assertSame('prefix', $reflParameters[1]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[1]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1114
        self::assertFalse($reflParameters[1]->isOptional());
1115
1116
        self::assertSame('groupNumber', $reflParameters[2]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[2]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1117
        self::assertFalse($reflParameters[2]->isOptional());
1118
1119
        self::assertSame('publisherNumber', $reflParameters[3]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[3]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1120
        self::assertFalse($reflParameters[3]->isOptional());
1121
1122
        self::assertSame('titleNumber', $reflParameters[4]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[4]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1123
        self::assertFalse($reflParameters[4]->isOptional());
1124
1125
        self::assertSame('checkDigit', $reflParameters[5]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[5]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1126
        self::assertFalse($reflParameters[5]->isOptional());
1127
1128
        $reflMethod = new \ReflectionMethod($test, '__construct');
1129
        $reflParameters = $reflMethod->getParameters();
1130
1131
        self::assertCount(4, $reflParameters);
1132
1133
        self::assertSame('field1', $reflParameters[0]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[0]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1134
        self::assertFalse($reflParameters[0]->isOptional());
1135
1136
        self::assertSame('DateTime', $reflParameters[1]->getClass()->name);
1137
        self::assertSame('field3', $reflParameters[1]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[1]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1138
        self::assertFalse($reflParameters[1]->isOptional());
1139
1140
        self::assertSame('field2', $reflParameters[2]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[2]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1141
        self::assertTrue($reflParameters[2]->isOptional());
1142
1143
        self::assertSame('DateTime', $reflParameters[3]->getClass()->name);
1144
        self::assertSame('field4', $reflParameters[3]->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflParameters[3]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1145
        self::assertTrue($reflParameters[3]->isOptional());
1146
    }
1147
1148
    public function testRegenerateEntityClass()
1149
    {
1150
        $metadata = $this->generateBookEntityFixture();
1151
        $this->loadEntityClass($metadata);
1152
1153
        $className = basename(str_replace('\\', '/', $metadata->getClassName()));
1154
        $path = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php';
1155
        $classTest = file_get_contents($path);
1156
1157
        $this->generator->setRegenerateEntityIfExists(true);
1158
        $this->generator->setBackupExisting(false);
1159
1160
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
1161
        $classNew = file_get_contents($path);
1162
1163
        self::assertSame($classTest,$classNew);
1164
    }
1165
1166
    /**
1167
     * @return array
1168
     */
1169
    public function getEntityTypeAliasDataProvider()
1170
    {
1171
        return [
1172
            [
1173
                [
1174
                'fieldName' => 'datetimetz',
1175
                'phpType' => '\\DateTime',
1176
                'dbType' => 'datetimetz',
1177
                'value' => new \DateTime
1178
                ]
1179
            ],
1180
            [
1181
                [
1182
                'fieldName' => 'datetime',
1183
                'phpType' => '\\DateTime',
1184
                'dbType' => 'datetime',
1185
                'value' => new \DateTime
1186
                ]
1187
            ],
1188
            [
1189
                [
1190
                'fieldName' => 'date',
1191
                'phpType' => '\\DateTime',
1192
                'dbType' => 'date',
1193
                'value' => new \DateTime
1194
                ]
1195
            ],
1196
            [
1197
                [
1198
                'fieldName' => 'time',
1199
                'phpType' => '\DateTime',
1200
                'dbType' => 'time',
1201
                'value' => new \DateTime
1202
                ]
1203
            ],
1204
            [
1205
                [
1206
                'fieldName' => 'object',
1207
                'phpType' => '\stdClass',
1208
                'dbType' => 'object',
1209
                'value' => new \stdClass()
1210
                ]
1211
            ],
1212
            [
1213
                [
1214
                'fieldName' => 'bigint',
1215
                'phpType' => 'int',
1216
                'dbType' => 'bigint',
1217
                'value' => 11
1218
                ]
1219
            ],
1220
            [
1221
                [
1222
                'fieldName' => 'smallint',
1223
                'phpType' => 'int',
1224
                'dbType' => 'smallint',
1225
                'value' => 22
1226
                ]
1227
            ],
1228
            [
1229
                [
1230
                'fieldName' => 'text',
1231
                'phpType' => 'string',
1232
                'dbType' => 'text',
1233
                'value' => 'text'
1234
                ]
1235
            ],
1236
            [
1237
                [
1238
                'fieldName' => 'blob',
1239
                'phpType' => 'string',
1240
                'dbType' => 'blob',
1241
                'value' => 'blob'
1242
                ]
1243
            ],
1244
            [
1245
                [
1246
                'fieldName' => 'decimal',
1247
                'phpType' => 'string',
1248
                'dbType' => 'decimal',
1249
                'value' => '12.34'
1250
                ],
1251
            ]
1252
        ];
1253
    }
1254
1255
    public function getParseTokensInEntityFileData()
1256
    {
1257
        return [
1258
            [
1259
                '<?php namespace Foo\Bar; class Baz {}',
1260
                ['Foo\Bar\Baz'],
1261
            ],
1262
            [
1263
                '<?php namespace Foo\Bar; use Foo; class Baz {}',
1264
                ['Foo\Bar\Baz'],
1265
            ],
1266
            [
1267
                '<?php namespace /*Comment*/ Foo\Bar; /** Foo */class /* Comment */ Baz {}',
1268
                ['Foo\Bar\Baz'],
1269
            ],
1270
            [
1271
                '
1272
<?php namespace
1273
/*Comment*/
1274
Foo\Bar
1275
;
1276
1277
/** Foo */
1278
class
1279
/* Comment */
1280
 Baz {}
1281
     ',
1282
                ['Foo\Bar\Baz'],
1283
            ],
1284
            [
1285
                '
1286
<?php namespace Foo\Bar; class Baz {
1287
    public static function someMethod(){
1288
        return self::class;
1289
    }
1290
}
1291
',
1292
                ['Foo\Bar\Baz'],
1293
            ],
1294
        ];
1295
    }
1296
1297
    /**
1298
     * @param string $type
1299
     * @param \ReflectionProperty $property
1300
     */
1301 View Code Duplication
    private function assertPhpDocVarType($type, \ReflectionProperty $property)
1302
    {
1303
        $docComment = $property->getDocComment();
1304
        $regex      = '/@var\s+([\S]+)$/m';
1305
1306
        self::assertRegExp($regex, $docComment);
1307
        self::assertEquals(1, preg_match($regex, $docComment, $matches));
1308
        self::assertEquals($type, $matches[1]);
1309
    }
1310
1311
    /**
1312
     * @param string $type
1313
     * @param \ReflectionMethod $method
1314
     */
1315 View Code Duplication
    private function assertPhpDocReturnType($type, \ReflectionMethod $method)
1316
    {
1317
        $docComment = $method->getDocComment();
1318
        $regex      = '/@return\s+([\S]+)(\s+.*)$/m';
1319
1320
        self::assertRegExp($regex, $docComment);
1321
        self::assertEquals(1, preg_match($regex, $docComment, $matches));
1322
        self::assertEquals($type, $matches[1]);
1323
    }
1324
1325
    /**
1326
     * @param string $type
1327
     * @param \ReflectionProperty $method
1328
     */
1329
    private function assertPhpDocParamType($type, \ReflectionMethod $method)
1330
    {
1331
        self::assertEquals(1, preg_match('/@param\s+([^\s]+)/', $method->getDocComment(), $matches));
1332
        self::assertEquals($type, $matches[1]);
1333
    }
1334
}
1335
1336
class EntityGeneratorAuthor {}
1337
class EntityGeneratorComment {}
1338