Test Setup Failed
Push — develop ( 082d66...6f26e1 )
by Guilherme
63:04
created

testMethodsAndPropertiesAreNotDuplicatedInChildClasses()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 70
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 70
rs 9.1724
c 0
b 0
f 0
cc 1
eloc 44
nc 1
nop 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
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\Tools\EntityGenerator;
16
use Doctrine\Tests\Models\DDC2372\DDC2372Admin;
17
use Doctrine\Tests\Models\DDC2372\DDC2372User;
18
use Doctrine\Tests\OrmTestCase;
19
20
class EntityGeneratorTest extends OrmTestCase
21
{
22
    /**
23
     * @var EntityGenerator
24
     */
25
    private $generator;
26
    private $tmpDir;
27
    private $namespace;
28
29
    /**
30
     * @var Mapping\ClassMetadataBuildingContext
31
     */
32
    private $metadataBuildingContext;
33
34 View Code Duplication
    public function setUp()
35
    {
36
        $this->metadataBuildingContext = new Mapping\ClassMetadataBuildingContext(
37
            $this->createMock(ClassMetadataFactory::class)
38
        );
39
        $this->namespace = uniqid("doctrine_", false);
40
        $this->tmpDir = sys_get_temp_dir();
41
42
        mkdir($this->tmpDir . \DIRECTORY_SEPARATOR . $this->namespace);
43
44
        $this->generator = new EntityGenerator();
45
46
        $this->generator->setGenerateAnnotations(true);
47
        $this->generator->setGenerateStubMethods(true);
48
        $this->generator->setRegenerateEntityIfExists(false);
49
        $this->generator->setUpdateEntityIfExists(true);
50
        $this->generator->setFieldVisibility(EntityGenerator::FIELD_VISIBLE_PROTECTED);
51
    }
52
53
    public function tearDown()
54
    {
55
        $ri = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir . '/' . $this->namespace));
56
57
        foreach ($ri AS $file) {
58
            /* @var $file \SplFileInfo */
59
            if ($file->isFile()) {
60
                unlink($file->getPathname());
61
            }
62
        }
63
64
        rmdir($this->tmpDir . '/' . $this->namespace);
65
    }
66
67
    /**
68
     * @param ClassMetadata[] $embeddedClasses
69
     *
70
     * @return ClassMetadata
71
     */
72
    public function generateBookEntityFixture(array $embeddedClasses = [])
73
    {
74
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook', $this->metadataBuildingContext);
75
76
        $metadata->setCustomRepositoryClassName($this->namespace . '\EntityGeneratorBookRepository');
77
78
        $tableMetadata = new Mapping\TableMetadata();
79
80
        $tableMetadata->setName('book');
81
        $tableMetadata->addUniqueConstraint(
82
            [
83
                'name'    => 'name_uniq',
84
                'columns' => ['name'],
85
                'options' => [],
86
                'flags'   => [],
87
            ]
88
        );
89
90
        $tableMetadata->addIndex(
91
            [
92
                'name'    => 'status_idx',
93
                'columns' => ['status'],
94
                'unique'  => false,
95
                'options' => [],
96
                'flags'   => [],
97
            ]
98
        );
99
100
        $metadata->setTable($tableMetadata);
101
102
        // Property: "name"
103
        $fieldMetadata = new Mapping\FieldMetadata('name');
104
105
        $fieldMetadata->setType(Type::getType('string'));
106
        $fieldMetadata->setNullable(false);
107
        $fieldMetadata->setUnique(false);
108
109
        $metadata->addProperty($fieldMetadata);
110
111
        // Property: "status"
112
        $fieldMetadata = new Mapping\FieldMetadata('status');
113
114
        $fieldMetadata->setType(Type::getType('string'));
115
        $fieldMetadata->setNullable(false);
116
        $fieldMetadata->setUnique(false);
117
        $fieldMetadata->setOptions([
118
            'default' => 'published',
119
        ]);
120
121
        $metadata->addProperty($fieldMetadata);
122
123
        // Property: "id"
124
        $fieldMetadata = new Mapping\FieldMetadata('id');
125
126
        $fieldMetadata->setType(Type::getType('integer'));
127
        $fieldMetadata->setPrimaryKey(true);
128
        $fieldMetadata->setNullable(false);
129
        $fieldMetadata->setUnique(false);
130
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::AUTO));
131
132
        $metadata->addProperty($fieldMetadata);
133
134
        // Property: "author"
135
        $joinColumns = [];
136
137
        $joinColumn = new Mapping\JoinColumnMetadata();
138
        $joinColumn->setColumnName('author_id');
139
        $joinColumn->setReferencedColumnName('id');
140
141
        $joinColumns[] = $joinColumn;
142
143
        $association = new Mapping\OneToOneAssociationMetadata('author');
144
145
        $association->setJoinColumns($joinColumns);
146
        $association->setTargetEntity(EntityGeneratorAuthor::class);
147
        $association->setMappedBy('book');
148
149
        $metadata->addProperty($association);
150
151
        // Property: "comments"
152
        $joinTable = new Mapping\JoinTableMetadata();
153
        $joinTable->setName('book_comment');
154
155
        $joinColumn = new Mapping\JoinColumnMetadata();
156
        $joinColumn->setColumnName("book_id");
157
        $joinColumn->setReferencedColumnName("id");
158
159
        $joinTable->addJoinColumn($joinColumn);
160
161
        $joinColumn = new Mapping\JoinColumnMetadata();
162
        $joinColumn->setColumnName("comment_id");
163
        $joinColumn->setReferencedColumnName("id");
164
165
        $joinTable->addInverseJoinColumn($joinColumn);
166
167
        $association = new Mapping\ManyToManyAssociationMetadata('comments');
168
169
        $association->setJoinTable($joinTable);
170
        $association->setTargetEntity(EntityGeneratorComment::class);
171
        $association->setFetchMode(Mapping\FetchMode::EXTRA_LAZY);
172
173
        $metadata->addProperty($association);
174
175
        $metadata->addLifecycleCallback('loading', 'postLoad');
176
        $metadata->addLifecycleCallback('willBeRemoved', 'preRemove');
177
178 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...
179
            $metadata->mapEmbedded(
180
                [
181
                    'fieldName'    => $fieldName,
182
                    'class'        => $embeddedClass->getClassName(),
183
                    'columnPrefix' => null,
184
                ]
185
            );
186
        }
187
188
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
189
190
        return $metadata;
191
    }
192
193
    private function generateEntityTypeFixture(array $field)
194
    {
195
        $metadata = new ClassMetadata($this->namespace . '\EntityType', $this->metadataBuildingContext);
196
197
        $tableMetadata = new Mapping\TableMetadata();
198
        $tableMetadata->setName('entity_type');
199
200
        $metadata->setTable($tableMetadata);
201
202
        $fieldMetadata = new Mapping\FieldMetadata('id');
203
        $fieldMetadata->setType(Type::getType('integer'));
204
        $fieldMetadata->setPrimaryKey(true);
205
        $fieldMetadata->setNullable(false);
206
        $fieldMetadata->setUnique(false);
207
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::AUTO));
208
209
        $metadata->addProperty($fieldMetadata);
210
211
        $fieldMetadata = new Mapping\FieldMetadata($field['fieldName']);
212
        $fieldMetadata->setType(Type::getType($field['dbType']));
213
        $fieldMetadata->setNullable(false);
214
        $fieldMetadata->setUnique(false);
215
216
        $metadata->addProperty($fieldMetadata);
217
218
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
219
220
        return $metadata;
221
    }
222
223
    /**
224
     * @return ClassMetadata
225
     */
226
    private function generateIsbnEmbeddableFixture(array $embeddedClasses = [], $columnPrefix = null)
227
    {
228
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorIsbn', $this->metadataBuildingContext);
229
        $metadata->isEmbeddedClass = true;
230
231
        $fieldMetadata = new Mapping\FieldMetadata('prefix');
232
        $fieldMetadata->setType(Type::getType('integer'));
233
        $fieldMetadata->setNullable(false);
234
        $fieldMetadata->setUnique(false);
235
236
        $metadata->addProperty($fieldMetadata);
237
238
        $fieldMetadata = new Mapping\FieldMetadata('groupNumber');
239
        $fieldMetadata->setType(Type::getType('integer'));
240
        $fieldMetadata->setNullable(false);
241
        $fieldMetadata->setUnique(false);
242
243
        $metadata->addProperty($fieldMetadata);
244
245
        $fieldMetadata = new Mapping\FieldMetadata('publisherNumber');
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('titleNumber');
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('checkDigit');
260
        $fieldMetadata->setType(Type::getType('integer'));
261
        $fieldMetadata->setNullable(false);
262
        $fieldMetadata->setUnique(false);
263
264
        $metadata->addProperty($fieldMetadata);
265
266 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...
267
            $metadata->mapEmbedded(
268
                [
269
                    'fieldName'    => $fieldName,
270
                    'class'        => $embeddedClass->getClassName(),
271
                    'columnPrefix' => $columnPrefix,
272
                ]
273
            );
274
        }
275
276
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
277
278
        return $metadata;
279
    }
280
281
    /**
282
     * @return ClassMetadata
283
     */
284
    private function generateTestEmbeddableFixture()
285
    {
286
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorTestEmbeddable', $this->metadataBuildingContext);
287
288
        $metadata->isEmbeddedClass = true;
289
290
        $fieldMetadata = new Mapping\FieldMetadata('field1');
291
        $fieldMetadata->setType(Type::getType('integer'));
292
        $fieldMetadata->setNullable(false);
293
        $fieldMetadata->setUnique(false);
294
295
        $metadata->addProperty($fieldMetadata);
296
297
        $fieldMetadata = new Mapping\FieldMetadata('field2');
298
        $fieldMetadata->setType(Type::getType('integer'));
299
        $fieldMetadata->setNullable(true);
300
        $fieldMetadata->setUnique(false);
301
302
        $metadata->addProperty($fieldMetadata);
303
304
        $fieldMetadata = new Mapping\FieldMetadata('field3');
305
        $fieldMetadata->setType(Type::getType('datetime'));
306
        $fieldMetadata->setNullable(false);
307
        $fieldMetadata->setUnique(false);
308
309
        $metadata->addProperty($fieldMetadata);
310
311
        $fieldMetadata = new Mapping\FieldMetadata('field4');
312
        $fieldMetadata->setType(Type::getType('datetime'));
313
        $fieldMetadata->setNullable(true);
314
        $fieldMetadata->setUnique(false);
315
316
        $metadata->addProperty($fieldMetadata);
317
318
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
319
320
        return $metadata;
321
    }
322
323
    /**
324
     * @param ClassMetadata $metadata
325
     */
326
    private function loadEntityClass(ClassMetadata $metadata)
327
    {
328
        $className = basename(str_replace('\\', '/', $metadata->getClassName()));
329
        $path      = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php';
330
331
        self::assertFileExists($path);
332
333
        require_once $path;
334
    }
335
336
    /**
337
     * @param  ClassMetadata $metadata
338
     *
339
     * @return mixed An instance of the given metadata's class.
340
     */
341
    public function newInstance(ClassMetadata $metadata)
342
    {
343
        $this->loadEntityClass($metadata);
344
345
        $className = $metadata->getClassName();
346
347
        return new $className;
348
    }
349
350
    /**
351
     * @group embedded
352
     * @group GH-6314
353
     */
354
    public function testEmbeddedEntityWithNamedColumnPrefix()
355
    {
356
        $columnPrefix = 'GH6314Prefix_';
357
        $testMetadata = $this->generateTestEmbeddableFixture();
358
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], $columnPrefix);
359
        $isbnEntity = $this->newInstance($isbnMetadata);
360
        $refClass = new \ReflectionClass($isbnEntity);
361
        self::assertTrue($refClass->hasProperty('testEmbedded'));
362
363
        $docComment = $refClass->getProperty('testEmbedded')->getDocComment();
364
        $needle = sprintf('@Embedded(class="%s", columnPrefix="%s")', $testMetadata->getClassName(), $columnPrefix);
365
        self::assertContains($needle, $docComment);
366
    }
367
368
    /**
369
     * @group embedded
370
     * @group GH-6314
371
     */
372
    public function testEmbeddedEntityWithoutColumnPrefix()
373
    {
374
        $testMetadata = $this->generateTestEmbeddableFixture();
375
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], false);
376
        $isbnEntity = $this->newInstance($isbnMetadata);
377
        $refClass = new \ReflectionClass($isbnEntity);
378
        self::assertTrue($refClass->hasProperty('testEmbedded'));
379
380
        $docComment = $refClass->getProperty('testEmbedded')->getDocComment();
381
        $needle = sprintf('@Embedded(class="%s", columnPrefix=false)', $testMetadata->getClassName());
382
        self::assertContains($needle, $docComment);
383
    }
384
385
    /**
386
     * @group embedded
387
     */
388
    public function testGeneratedEntityClass()
389
    {
390
        $testMetadata = $this->generateTestEmbeddableFixture();
391
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(['test' => $testMetadata]);
392
        $metadata     = $this->generateBookEntityFixture(['isbn' => $isbnMetadata]);
393
        $book         = $this->newInstance($metadata);
394
395
        $bookClassName = $metadata->getClassName();
396
397
        self::assertTrue(class_exists($bookClassName), "Class does not exist.");
398
        self::assertTrue(method_exists($bookClassName, '__construct'), "EntityGeneratorBook::__construct() missing.");
399
        self::assertTrue(method_exists($bookClassName, 'getId'), "EntityGeneratorBook::getId() missing.");
400
        self::assertTrue(method_exists($bookClassName, 'setName'), "EntityGeneratorBook::setName() missing.");
401
        self::assertTrue(method_exists($bookClassName, 'getName'), "EntityGeneratorBook::getName() missing.");
402
        self::assertTrue(method_exists($bookClassName, 'setStatus'), "EntityGeneratorBook::setStatus() missing.");
403
        self::assertTrue(method_exists($bookClassName, 'getStatus'), "EntityGeneratorBook::getStatus() missing.");
404
        self::assertTrue(method_exists($bookClassName, 'setAuthor'), "EntityGeneratorBook::setAuthor() missing.");
405
        self::assertTrue(method_exists($bookClassName, 'getAuthor'), "EntityGeneratorBook::getAuthor() missing.");
406
        self::assertTrue(method_exists($bookClassName, 'getComments'), "EntityGeneratorBook::getComments() missing.");
407
        self::assertTrue(method_exists($bookClassName, 'addComment'), "EntityGeneratorBook::addComment() missing.");
408
        self::assertTrue(method_exists($bookClassName, 'removeComment'), "EntityGeneratorBook::removeComment() missing.");
409
        self::assertTrue(method_exists($bookClassName, 'setIsbn'), "EntityGeneratorBook::setIsbn() missing.");
410
        self::assertTrue(method_exists($bookClassName, 'getIsbn'), "EntityGeneratorBook::getIsbn() missing.");
411
412
        $reflClass = new \ReflectionClass($metadata->getClassName());
413
414
        self::assertCount(6, $reflClass->getProperties());
415
        self::assertCount(15, $reflClass->getMethods());
416
417
        self::assertEquals('published', $book->getStatus());
418
419
        $book->setName('Jonathan H. Wage');
420
        self::assertEquals('Jonathan H. Wage', $book->getName());
421
422
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'addComment');
423
        $addCommentParameters = $reflMethod->getParameters();
424
        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...
425
426
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'removeComment');
427
        $removeCommentParameters = $reflMethod->getParameters();
428
        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...
429
430
        $author = new EntityGeneratorAuthor();
431
        $book->setAuthor($author);
432
        self::assertEquals($author, $book->getAuthor());
433
434
        $comment = new EntityGeneratorComment();
435
        self::assertInstanceOf($metadata->getClassName(), $book->addComment($comment));
436
        self::assertInstanceOf(ArrayCollection::class, $book->getComments());
437
        self::assertEquals(new ArrayCollection([$comment]), $book->getComments());
438
        self::assertInternalType('boolean', $book->removeComment($comment));
439
        self::assertEquals(new ArrayCollection([]), $book->getComments());
440
441
        $this->newInstance($isbnMetadata);
442
443
        $isbnClassName = $isbnMetadata->getClassName();
444
445
        $isbn = new $isbnClassName();
446
447
        $book->setIsbn($isbn);
448
        self::assertSame($isbn, $book->getIsbn());
449
450
        $reflMethod = new \ReflectionMethod($metadata->getClassName(), 'setIsbn');
451
        $reflParameters = $reflMethod->getParameters();
452
        self::assertEquals($isbnMetadata->getClassName(), $reflParameters[0]->getClass()->name);
453
    }
454
455
    /**
456
     * @group embedded
457
     */
458
    public function testEntityUpdatingWorks()
459
    {
460
        $metadata = $this->generateBookEntityFixture(['isbn' => $this->generateIsbnEmbeddableFixture()]);
461
462
        $fieldMetadata = new Mapping\FieldMetadata('test');
463
464
        $fieldMetadata->setType(Type::getType('string'));
465
466
        $metadata->addProperty($fieldMetadata);
467
468
        $testEmbeddableMetadata = $this->generateTestEmbeddableFixture();
469
470
        $metadata->mapEmbedded(
471
            [
472
            'fieldName'    => 'testEmbedded',
473
            'class'        => $testEmbeddableMetadata->getClassName(),
474
            'columnPrefix' => null,
475
            ]
476
        );
477
478
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
479
480
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/EntityGeneratorBook.php~");
481
482
        $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...
483
        $reflClass = new \ReflectionClass($metadata->getClassName());
484
485
        self::assertTrue($reflClass->hasProperty('name'), "Regenerating keeps property 'name'.");
486
        self::assertTrue($reflClass->hasProperty('status'), "Regenerating keeps property 'status'.");
487
        self::assertTrue($reflClass->hasProperty('id'), "Regenerating keeps property 'id'.");
488
        self::assertTrue($reflClass->hasProperty('isbn'), "Regenerating keeps property 'isbn'.");
489
490
        self::assertTrue($reflClass->hasProperty('test'), "Check for property test failed.");
491
        self::assertTrue($reflClass->getProperty('test')->isProtected(), "Check for protected property test failed.");
492
        self::assertTrue($reflClass->hasProperty('testEmbedded'), "Check for property testEmbedded failed.");
493
        self::assertTrue($reflClass->getProperty('testEmbedded')->isProtected(), "Check for protected property testEmbedded failed.");
494
        self::assertTrue($reflClass->hasMethod('getTest'), "Check for method 'getTest' failed.");
495
        self::assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed.");
496
        self::assertTrue($reflClass->hasMethod('setTest'), "Check for method 'setTest' failed.");
497
        self::assertTrue($reflClass->getMethod('setTest')->isPublic(), "Check for public visibility of method 'setTest' failed.");
498
        self::assertTrue($reflClass->hasMethod('getTestEmbedded'), "Check for method 'getTestEmbedded' failed.");
499
        self::assertTrue(
500
            $reflClass->getMethod('getTestEmbedded')->isPublic(),
501
            "Check for public visibility of method 'getTestEmbedded' failed."
502
        );
503
        self::assertTrue($reflClass->hasMethod('setTestEmbedded'), "Check for method 'setTestEmbedded' failed.");
504
        self::assertTrue(
505
            $reflClass->getMethod('setTestEmbedded')->isPublic(),
506
            "Check for public visibility of method 'setTestEmbedded' failed."
507
        );
508
    }
509
510
    /**
511
     * @group DDC-2121
512
     */
513
    public function testMethodDocBlockShouldStartWithBackSlash()
514
    {
515
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
516
        $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
517
        $book     = $this->newInstance($metadata);
518
519
        self::assertPhpDocVarType('\Doctrine\Common\Collections\Collection', new \ReflectionProperty($book, 'comments'));
520
        self::assertPhpDocReturnType('\Doctrine\Common\Collections\Collection', new \ReflectionMethod($book, 'getComments'));
521
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'addComment'));
522
        self::assertPhpDocReturnType('EntityGeneratorBook', new \ReflectionMethod($book, 'addComment'));
523
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'removeComment'));
524
        self::assertPhpDocReturnType('boolean', new \ReflectionMethod($book, 'removeComment'));
525
526
        self::assertPhpDocVarType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new \ReflectionProperty($book, 'author'));
527
        self::assertPhpDocReturnType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new \ReflectionMethod($book, 'getAuthor'));
528
        self::assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new \ReflectionMethod($book, 'setAuthor'));
529
530
//        $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...
531
//        self::assertPhpDocVarType($expectedClassName, new \ReflectionProperty($book, 'isbn'));
532
//        self::assertPhpDocReturnType($expectedClassName, new \ReflectionMethod($book, 'getIsbn'));
533
//        self::assertPhpDocParamType($expectedClassName, new \ReflectionMethod($book, 'setIsbn'));
534
    }
535
536
    public function testEntityExtendsStdClass()
537
    {
538
        $this->generator->setClassToExtend('stdClass');
539
        $metadata = $this->generateBookEntityFixture();
540
541
        $book = $this->newInstance($metadata);
542
        self::assertInstanceOf('stdClass', $book);
543
544
        $metadata = $this->generateIsbnEmbeddableFixture();
545
        $isbn = $this->newInstance($metadata);
546
        self::assertInstanceOf('stdClass', $isbn);
547
    }
548
549
    public function testLifecycleCallbacks()
550
    {
551
        $metadata = $this->generateBookEntityFixture();
552
553
        $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...
554
        $reflClass = new \ReflectionClass($metadata->getClassName());
555
556
        self::assertTrue($reflClass->hasMethod('loading'), "Check for postLoad lifecycle callback.");
557
        self::assertTrue($reflClass->hasMethod('willBeRemoved'), "Check for preRemove lifecycle callback.");
558
    }
559
560
    public function testLoadMetadata()
561
    {
562
        $reflectionService = new RuntimeReflectionService();
563
564
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
565
        $metadata         = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
566
        $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...
567
568
        $metadata->initializeReflection($reflectionService);
569
570
        $cm = new ClassMetadata($metadata->getClassName(), $this->metadataBuildingContext);
571
        $cm->initializeReflection($reflectionService);
572
573
        $driver = $this->createAnnotationDriver();
574
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->metadataBuildingContext);
575
576
        self::assertEquals($cm->getTableName(), $metadata->getTableName());
577
        self::assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
578
        self::assertEquals($cm->identifier, $metadata->identifier);
579
        self::assertEquals($cm->getCustomRepositoryClassName(), $metadata->getCustomRepositoryClassName());
580
//        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...
581
//        self::assertEquals($cm->isEmbeddedClass, $metadata->isEmbeddedClass);
582
583
        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...
584
585
//        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
586
//
587
//        $cm = new ClassMetadata($embeddedMetadata->getClassName());
588
//        $cm->initializeReflection($reflectionService);
589
//
590
//        $driver->loadMetadataForClass($cm->getClassName(), $cm);
591
//
592
//        self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
593
//        self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
594
    }
595
596
    public function testLoadPrefixedMetadata()
597
    {
598
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
599
        $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]);
600
601
        $reader = new AnnotationReader();
602
        $driver = new AnnotationDriver($reader, []);
603
604
        $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...
605
606
        $cm = new ClassMetadata($metadata->getClassName(), $this->metadataBuildingContext);
607
        $cm->initializeReflection(new RuntimeReflectionService());
608
609
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->metadataBuildingContext);
610
611
        self::assertEquals($cm->getTableName(), $metadata->getTableName());
612
        self::assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
613
        self::assertEquals($cm->identifier, $metadata->identifier);
614
        self::assertEquals($cm->getCustomRepositoryClassName(), $metadata->getCustomRepositoryClassName());
615
616
//        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
617
//
618
//        $cm = new ClassMetadata($embeddedMetadata->getClassName());
619
//        $cm->initializeReflection($reflectionService);
620
//
621
//        $driver->loadMetadataForClass($cm->getClassName(), $cm);
622
//
623
//        self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
624
//        self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
625
    }
626
627
    /**
628
     * @group DDC-3272
629
     */
630
    public function testMappedSuperclassAnnotationGeneration()
631
    {
632
        $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook', $this->metadataBuildingContext);
633
634
        $metadata->isMappedSuperclass = true;
635
636
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
637
638
        $this->newInstance($metadata); // force instantiation (causes autoloading to kick in)
639
640
        $driver = new AnnotationDriver(new AnnotationReader(), []);
641
        $cm     = new ClassMetadata($metadata->getClassName(), $this->metadataBuildingContext);
642
643
        $cm->initializeReflection(new RuntimeReflectionService());
644
        $driver->loadMetadataForClass($cm->getClassName(), $cm, $this->metadataBuildingContext);
645
646
        self::assertTrue($cm->isMappedSuperclass);
647
    }
648
649
    /**
650
     * @dataProvider getParseTokensInEntityFileData
651
     */
652
    public function testParseTokensInEntityFile($php, $classes)
653
    {
654
        $r = new \ReflectionObject($this->generator);
655
        $m = $r->getMethod('parseTokensInEntityFile');
656
        $m->setAccessible(true);
657
658
        $p = $r->getProperty('staticReflection');
659
        $p->setAccessible(true);
660
661
        $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...
662
        self::assertEquals($classes, array_keys($p->getValue($this->generator)));
663
    }
664
665
    /**
666
     * @group DDC-1784
667
     */
668
    public function testGenerateEntityWithSequenceGenerator()
669
    {
670
        $metadata = new ClassMetadata($this->namespace . '\DDC1784Entity', $this->metadataBuildingContext);
671
672
        $fieldMetadata = new Mapping\FieldMetadata('id');
673
        $fieldMetadata->setType(Type::getType('integer'));
674
        $fieldMetadata->setPrimaryKey(true);
675
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(
676
            Mapping\GeneratorType::SEQUENCE,
677
            [
678
                'sequenceName'   => 'DDC1784_ID_SEQ',
679
                'allocationSize' => 1,
680
            ]
681
        ));
682
683
        $metadata->addProperty($fieldMetadata);
684
685
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
686
687
        $filename = $this->tmpDir . DIRECTORY_SEPARATOR
688
                  . $this->namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php';
689
690
        self::assertFileExists($filename);
691
692
        require_once $filename;
693
694
        $reflection = new \ReflectionProperty($metadata->getClassName(), 'id');
695
        $docComment = $reflection->getDocComment();
696
697
        self::assertContains('@ORM\Id', $docComment);
698
        self::assertContains('@ORM\Column(name="id", type="integer")', $docComment);
699
        self::assertContains('@ORM\GeneratedValue(strategy="SEQUENCE")', $docComment);
700
        self::assertContains('@ORM\SequenceGenerator(sequenceName="DDC1784_ID_SEQ", allocationSize=1)', $docComment);
701
    }
702
703
    /**
704
     * @group DDC-2079
705
     */
706
    public function testGenerateEntityWithMultipleInverseJoinColumns()
707
    {
708
        $metadata = new ClassMetadata($this->namespace . '\DDC2079Entity', $this->metadataBuildingContext);
709
710
        $fieldMetadata = new Mapping\FieldMetadata('id');
711
        $fieldMetadata->setType(Type::getType('integer'));
712
        $fieldMetadata->setPrimaryKey(true);
713
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(Mapping\GeneratorType::SEQUENCE));
714
715
        $metadata->addProperty($fieldMetadata);
716
717
        $joinTable = new Mapping\JoinTableMetadata();
718
        $joinTable->setName('unidade_centro_custo');
719
720
        $joinColumn = new Mapping\JoinColumnMetadata();
721
        $joinColumn->setColumnName("idorcamento");
722
        $joinColumn->setReferencedColumnName("idorcamento");
723
724
        $joinTable->addJoinColumn($joinColumn);
725
726
        $joinColumn = new Mapping\JoinColumnMetadata();
727
        $joinColumn->setColumnName("idunidade");
728
        $joinColumn->setReferencedColumnName("idunidade");
729
730
        $joinTable->addJoinColumn($joinColumn);
731
732
        $joinColumn = new Mapping\JoinColumnMetadata();
733
        $joinColumn->setColumnName("idcentrocusto");
734
        $joinColumn->setReferencedColumnName("idcentrocusto");
735
736
        $joinTable->addInverseJoinColumn($joinColumn);
737
738
        $joinColumn = new Mapping\JoinColumnMetadata();
739
        $joinColumn->setColumnName("idpais");
740
        $joinColumn->setReferencedColumnName("idpais");
741
742
        $joinTable->addInverseJoinColumn($joinColumn);
743
744
        $association = new Mapping\ManyToManyAssociationMetadata('centroCustos');
745
746
        $association->setJoinTable($joinTable);
747
        $association->setTargetEntity($this->namespace . '\\DDC2079CentroCusto');
748
749
        $metadata->addProperty($association);
750
751
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
752
753
        $filename = $this->tmpDir . DIRECTORY_SEPARATOR
754
            . $this->namespace . DIRECTORY_SEPARATOR . 'DDC2079Entity.php';
755
756
        self::assertFileExists($filename);
757
758
        require_once $filename;
759
760
        $property   = new \ReflectionProperty($metadata->getClassName(), 'centroCustos');
761
        $docComment = $property->getDocComment();
762
763
        //joinColumns
764
        self::assertContains('@ORM\JoinColumn(name="idorcamento", referencedColumnName="idorcamento"),', $docComment);
765
        self::assertContains('@ORM\JoinColumn(name="idunidade", referencedColumnName="idunidade")', $docComment);
766
        //inverseJoinColumns
767
        self::assertContains('@ORM\JoinColumn(name="idcentrocusto", referencedColumnName="idcentrocusto"),', $docComment);
768
        self::assertContains('@ORM\JoinColumn(name="idpais", referencedColumnName="idpais")', $docComment);
769
770
    }
771
772
     /**
773
     * @group DDC-2172
774
     */
775 View Code Duplication
    public function testGetInheritanceTypeString()
776
    {
777
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
778
        $method     = new \ReflectionMethod($this->generator, 'getInheritanceTypeString');
779
        $constants  = $reflection->getConstants();
780
        $pattern    = '/^InheritanceType::/';
781
782
        $method->setAccessible(true);
783
784
        foreach ($constants as $name => $value) {
785
            if( ! preg_match($pattern, $name)) {
786
                continue;
787
            }
788
789
            $expected = preg_replace($pattern, '', $name);
790
            $actual   = $method->invoke($this->generator, $value);
791
792
            self::assertEquals($expected, $actual);
793
        }
794
795
        $this->expectException(\InvalidArgumentException::class);
796
        $this->expectExceptionMessage('Invalid provided InheritanceType: INVALID');
797
798
        $method->invoke($this->generator, 'INVALID');
799
    }
800
801
    /**
802
    * @group DDC-2172
803
    */
804 View Code Duplication
    public function testGetChangeTrackingPolicyString()
805
    {
806
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
807
        $method     = new \ReflectionMethod($this->generator, 'getChangeTrackingPolicyString');
808
        $constants  = $reflection->getConstants();
809
        $pattern    = '/^ChangeTrackingPolicy::/';
810
811
        $method->setAccessible(true);
812
813
        foreach ($constants as $name => $value) {
814
            if( ! preg_match($pattern, $name)) {
815
                continue;
816
            }
817
818
            $expected = preg_replace($pattern, '', $name);
819
            $actual   = $method->invoke($this->generator, $value);
820
821
            self::assertEquals($expected, $actual);
822
        }
823
824
        $this->expectException(\InvalidArgumentException::class);
825
        $this->expectExceptionMessage('Invalid provided ChangeTrackingPolicy: INVALID');
826
827
        $method->invoke($this->generator, 'INVALID');
828
    }
829
830
    /**
831
     * @group DDC-2172
832
     */
833 View Code Duplication
    public function testGetIdGeneratorTypeString()
834
    {
835
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
836
        $method     = new \ReflectionMethod($this->generator, 'getIdGeneratorTypeString');
837
        $constants  = $reflection->getConstants();
838
        $pattern    = '/^GeneratorType::/';
839
840
        $method->setAccessible(true);
841
842
        foreach ($constants as $name => $value) {
843
            if( ! preg_match($pattern, $name)) {
844
                continue;
845
            }
846
847
            $expected = preg_replace($pattern, '', $name);
848
            $actual   = $method->invoke($this->generator, $value);
849
850
            self::assertEquals($expected, $actual);
851
        }
852
853
        $this->expectException(\InvalidArgumentException::class);
854
        $this->expectExceptionMessage('Invalid provided IdGeneratorType: INVALID');
855
856
        $method->invoke($this->generator, 'INVALID');
857
    }
858
859
    /**
860
     * @dataProvider getEntityTypeAliasDataProvider
861
     *
862
     * @group DDC-1694
863
     */
864
    public function testEntityTypeAlias(array $field)
865
    {
866
        $metadata   = $this->generateEntityTypeFixture($field);
867
        $path       = $this->tmpDir . '/'. $this->namespace . '/EntityType.php';
868
869
        self::assertFileExists($path);
870
        require_once $path;
871
872
        $entityClassName = $metadata->getClassName();
873
874
        $entity     = new $entityClassName;
875
        $reflClass  = new \ReflectionClass($entityClassName);
876
877
        $type   = $field['phpType'];
878
        $name   = $field['fieldName'];
879
        $value  = $field['value'];
880
        $getter = "get" . ucfirst($name);
881
        $setter = "set" . ucfirst($name);
882
883
        self::assertPhpDocVarType($type, $reflClass->getProperty($name));
884
        self::assertPhpDocParamType($type, $reflClass->getMethod($setter));
885
        self::assertPhpDocReturnType($type, $reflClass->getMethod($getter));
886
887
        self::assertSame($entity, $entity->{$setter}($value));
888
        self::assertEquals($value, $entity->{$getter}());
889
    }
890
891
    /**
892
     * @group DDC-2372
893
     */
894 View Code Duplication
    public function testTraitPropertiesAndMethodsAreNotDuplicated()
895
    {
896
        $cmf = new ClassMetadataFactory();
897
        $em = $this->getTestEntityManager();
898
        $cmf->setEntityManager($em);
899
900
        $user = new DDC2372User();
901
        $metadata = $cmf->getMetadataFor(get_class($user));
902
903
        // @todo guilhermeblanco Fix this test as changing Entity class should never be allowed.
904
        $metadata->setClassName($this->namespace . "\DDC2372User");
905
906
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
907
908
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/DDC2372User.php");
909
910
        require $this->tmpDir . "/" . $this->namespace . "/DDC2372User.php";
911
912
        $reflClass = new \ReflectionClass($metadata->getClassName());
913
914
        self::assertSame($reflClass->hasProperty('address'), false);
915
        self::assertSame($reflClass->hasMethod('setAddress'), false);
916
        self::assertSame($reflClass->hasMethod('getAddress'), false);
917
    }
918
919
    /**
920
     * @group DDC-2372
921
     */
922 View Code Duplication
    public function testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses()
923
    {
924
        $cmf = new ClassMetadataFactory();
925
        $em = $this->getTestEntityManager();
926
        $cmf->setEntityManager($em);
927
928
        $user = new DDC2372Admin();
929
        $metadata = $cmf->getMetadataFor(get_class($user));
930
931
        // @todo guilhermeblanco Fix this test as changing Entity class should never be allowed.
932
        $metadata->setClassName($this->namespace . "\DDC2372Admin");
933
934
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
935
936
        self::assertFileExists($this->tmpDir . "/" . $this->namespace . "/DDC2372Admin.php");
937
        require $this->tmpDir . "/" . $this->namespace . "/DDC2372Admin.php";
938
939
        $reflClass = new \ReflectionClass($metadata->getClassName());
940
941
        self::assertSame($reflClass->hasProperty('address'), false);
942
        self::assertSame($reflClass->hasMethod('setAddress'), false);
943
        self::assertSame($reflClass->hasMethod('getAddress'), false);
944
    }
945
946
    /**
947
     * @group DDC-1590
948
     */
949
    public function testMethodsAndPropertiesAreNotDuplicatedInChildClasses()
950
    {
951
        $cmf    = new ClassMetadataFactory();
952
        $em     = $this->getTestEntityManager();
953
954
        $cmf->setEntityManager($em);
955
956
        $ns     = $this->namespace;
957
        $nsdir  = $this->tmpDir . '/' . $ns;
958
959
        // Dump DDC1590User into temp file
960
        $content = str_replace(
961
            'namespace Doctrine\Tests\Models\DDC1590',
962
            'namespace ' . $ns,
963
            file_get_contents(__DIR__ . '/../../Models/DDC1590/DDC1590User.php')
964
        );
965
966
        $fname = $nsdir . "/DDC1590User.php";
967
        file_put_contents($fname, $content);
968
969
        // Require DDC1590User
970
        require $fname;
971
972
        $metadata = $cmf->getMetadataFor($ns . '\DDC1590User');
973
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
974
975
        // 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...
976
        $source = file_get_contents($fname);
977
978
        // 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...
979
        $source2    = str_replace('class DDC1590User', 'class _DDC1590User', $source);
980
        $fname2     = $nsdir . "/_DDC1590User.php";
981
        file_put_contents($fname2, $source2);
982
        require $fname2;
983
984
        // 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...
985
        $source3    = str_replace('class DDC1590User extends DDC1590Entity', 'class __DDC1590User', $source);
986
        $fname3     = $nsdir . "/__DDC1590User.php";
987
        file_put_contents($fname3, $source3);
988
        require $fname3;
989
990
991
        // 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...
992
        $rc2 = new \ReflectionClass($ns.'\_DDC1590User');
993
994
        self::assertTrue($rc2->hasProperty('name'));
995
        self::assertTrue($rc2->hasProperty('id'));
996
        self::assertTrue($rc2->hasProperty('created_at'));
997
998
        self::assertTrue($rc2->hasMethod('getName'));
999
        self::assertTrue($rc2->hasMethod('setName'));
1000
        self::assertTrue($rc2->hasMethod('getId'));
1001
        self::assertFalse($rc2->hasMethod('setId'));
1002
        self::assertTrue($rc2->hasMethod('getCreatedAt'));
1003
        self::assertTrue($rc2->hasMethod('setCreatedAt'));
1004
1005
        // 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...
1006
        $rc3 = new \ReflectionClass($ns.'\__DDC1590User');
1007
1008
        self::assertTrue($rc3->hasProperty('name'));
1009
        self::assertFalse($rc3->hasProperty('id'));
1010
        self::assertFalse($rc3->hasProperty('created_at'));
1011
1012
        self::assertTrue($rc3->hasMethod('getName'));
1013
        self::assertTrue($rc3->hasMethod('setName'));
1014
        self::assertFalse($rc3->hasMethod('getId'));
1015
        self::assertFalse($rc3->hasMethod('setId'));
1016
        self::assertFalse($rc3->hasMethod('getCreatedAt'));
1017
        self::assertFalse($rc3->hasMethod('setCreatedAt'));
1018
    }
1019
1020
    /**
1021
     * @group embedded
1022
     * @group DDC-3304
1023
     */
1024
    public function testGeneratedMutableEmbeddablesClass()
1025
    {
1026
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
1027
        $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]);
1028
1029
        $isbn = $this->newInstance($metadata);
1030
        $isbnClassName = $metadata->getClassName();
1031
1032
        self::assertTrue(class_exists($isbnClassName), "Class does not exist.");
1033
        self::assertFalse(method_exists($isbnClassName, '__construct'), "EntityGeneratorIsbn::__construct present.");
1034
        self::assertTrue(method_exists($isbnClassName, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
1035
        self::assertTrue(method_exists($isbnClassName, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() missing.");
1036
        self::assertTrue(method_exists($isbnClassName, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
1037
        self::assertTrue(method_exists($isbnClassName, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() missing.");
1038
        self::assertTrue(method_exists($isbnClassName, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
1039
        self::assertTrue(method_exists($isbnClassName, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() missing.");
1040
        self::assertTrue(method_exists($isbnClassName, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
1041
        self::assertTrue(method_exists($isbnClassName, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() missing.");
1042
        self::assertTrue(method_exists($isbnClassName, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
1043
        self::assertTrue(method_exists($isbnClassName, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() missing.");
1044
        self::assertTrue(method_exists($isbnClassName, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
1045
        self::assertTrue(method_exists($isbnClassName, 'setTest'), "EntityGeneratorIsbn::setTest() missing.");
1046
1047
        $isbn->setPrefix(978);
1048
        self::assertSame(978, $isbn->getPrefix());
1049
1050
        $this->newInstance($embeddedMetadata);
1051
1052
        $testEmbeddedCLassName = $embeddedMetadata->getClassName();
1053
1054
        $test = new $testEmbeddedCLassName();
1055
1056
        $isbn->setTest($test);
1057
        self::assertSame($test, $isbn->getTest());
1058
1059
        $reflMethod = new \ReflectionMethod($isbnClassName, 'setTest');
1060
        $reflParameters = $reflMethod->getParameters();
1061
        self::assertEquals($embeddedMetadata->getClassName(), $reflParameters[0]->getClass()->name);
1062
    }
1063
1064
    /**
1065
     * @group embedded
1066
     * @group DDC-3304
1067
     */
1068
    public function testGeneratedImmutableEmbeddablesClass()
1069
    {
1070
        $this->generator->setEmbeddablesImmutable(true);
1071
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
1072
        $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]);
1073
1074
        $this->loadEntityClass($embeddedMetadata);
1075
        $this->loadEntityClass($metadata);
1076
1077
        $isbnClassName = $metadata->getClassName();
1078
1079
        self::assertTrue(class_exists($isbnClassName), "Class does not exist.");
1080
        self::assertTrue(method_exists($isbnClassName, '__construct'), "EntityGeneratorIsbn::__construct missing.");
1081
        self::assertTrue(method_exists($isbnClassName, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
1082
        self::assertFalse(method_exists($isbnClassName, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() present.");
1083
        self::assertTrue(method_exists($isbnClassName, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
1084
        self::assertFalse(method_exists($isbnClassName, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() present.");
1085
        self::assertTrue(method_exists($isbnClassName, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
1086
        self::assertFalse(method_exists($isbnClassName, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() present.");
1087
        self::assertTrue(method_exists($isbnClassName, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
1088
        self::assertFalse(method_exists($isbnClassName, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() present.");
1089
        self::assertTrue(method_exists($isbnClassName, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
1090
        self::assertFalse(method_exists($isbnClassName, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() present.");
1091
        self::assertTrue(method_exists($isbnClassName, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
1092
        self::assertFalse(method_exists($isbnClassName, 'setTest'), "EntityGeneratorIsbn::setTest() present.");
1093
1094
        $embeddedClassName = $embeddedMetadata->getClassName();
1095
1096
        $test = new $embeddedClassName(1, new \DateTime());
1097
        $isbn = new $isbnClassName($test, 978, 3, 12, 732320, 83);
1098
1099
        $reflMethod = new \ReflectionMethod($isbn, '__construct');
1100
        $reflParameters = $reflMethod->getParameters();
1101
1102
        self::assertCount(6, $reflParameters);
1103
1104
        self::assertSame($embeddedMetadata->getClassName(), $reflParameters[0]->getClass()->name);
1105
        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...
1106
        self::assertFalse($reflParameters[0]->isOptional());
1107
1108
        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...
1109
        self::assertFalse($reflParameters[1]->isOptional());
1110
1111
        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...
1112
        self::assertFalse($reflParameters[2]->isOptional());
1113
1114
        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...
1115
        self::assertFalse($reflParameters[3]->isOptional());
1116
1117
        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...
1118
        self::assertFalse($reflParameters[4]->isOptional());
1119
1120
        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...
1121
        self::assertFalse($reflParameters[5]->isOptional());
1122
1123
        $reflMethod = new \ReflectionMethod($test, '__construct');
1124
        $reflParameters = $reflMethod->getParameters();
1125
1126
        self::assertCount(4, $reflParameters);
1127
1128
        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...
1129
        self::assertFalse($reflParameters[0]->isOptional());
1130
1131
        self::assertSame('DateTime', $reflParameters[1]->getClass()->name);
1132
        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...
1133
        self::assertFalse($reflParameters[1]->isOptional());
1134
1135
        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...
1136
        self::assertTrue($reflParameters[2]->isOptional());
1137
1138
        self::assertSame('DateTime', $reflParameters[3]->getClass()->name);
1139
        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...
1140
        self::assertTrue($reflParameters[3]->isOptional());
1141
    }
1142
1143
    public function testRegenerateEntityClass()
1144
    {
1145
        $metadata = $this->generateBookEntityFixture();
1146
        $this->loadEntityClass($metadata);
1147
1148
        $className = basename(str_replace('\\', '/', $metadata->getClassName()));
1149
        $path = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php';
1150
        $classTest = file_get_contents($path);
1151
1152
        $this->generator->setRegenerateEntityIfExists(true);
1153
        $this->generator->setBackupExisting(false);
1154
1155
        $this->generator->writeEntityClass($metadata, $this->tmpDir);
1156
        $classNew = file_get_contents($path);
1157
1158
        self::assertSame($classTest,$classNew);
1159
    }
1160
1161
    /**
1162
     * @return array
1163
     */
1164
    public function getEntityTypeAliasDataProvider()
1165
    {
1166
        return [
1167
            [
1168
                [
1169
                'fieldName' => 'datetimetz',
1170
                'phpType' => '\\DateTime',
1171
                'dbType' => 'datetimetz',
1172
                'value' => new \DateTime
1173
                ]
1174
            ],
1175
            [
1176
                [
1177
                'fieldName' => 'datetime',
1178
                'phpType' => '\\DateTime',
1179
                'dbType' => 'datetime',
1180
                'value' => new \DateTime
1181
                ]
1182
            ],
1183
            [
1184
                [
1185
                'fieldName' => 'date',
1186
                'phpType' => '\\DateTime',
1187
                'dbType' => 'date',
1188
                'value' => new \DateTime
1189
                ]
1190
            ],
1191
            [
1192
                [
1193
                'fieldName' => 'time',
1194
                'phpType' => '\DateTime',
1195
                'dbType' => 'time',
1196
                'value' => new \DateTime
1197
                ]
1198
            ],
1199
            [
1200
                [
1201
                'fieldName' => 'object',
1202
                'phpType' => '\stdClass',
1203
                'dbType' => 'object',
1204
                'value' => new \stdClass()
1205
                ]
1206
            ],
1207
            [
1208
                [
1209
                'fieldName' => 'bigint',
1210
                'phpType' => 'int',
1211
                'dbType' => 'bigint',
1212
                'value' => 11
1213
                ]
1214
            ],
1215
            [
1216
                [
1217
                'fieldName' => 'smallint',
1218
                'phpType' => 'int',
1219
                'dbType' => 'smallint',
1220
                'value' => 22
1221
                ]
1222
            ],
1223
            [
1224
                [
1225
                'fieldName' => 'text',
1226
                'phpType' => 'string',
1227
                'dbType' => 'text',
1228
                'value' => 'text'
1229
                ]
1230
            ],
1231
            [
1232
                [
1233
                'fieldName' => 'blob',
1234
                'phpType' => 'string',
1235
                'dbType' => 'blob',
1236
                'value' => 'blob'
1237
                ]
1238
            ],
1239
            [
1240
                [
1241
                'fieldName' => 'decimal',
1242
                'phpType' => 'string',
1243
                'dbType' => 'decimal',
1244
                'value' => '12.34'
1245
                ],
1246
            ]
1247
        ];
1248
    }
1249
1250
    public function getParseTokensInEntityFileData()
1251
    {
1252
        return [
1253
            [
1254
                '<?php namespace Foo\Bar; class Baz {}',
1255
                ['Foo\Bar\Baz'],
1256
            ],
1257
            [
1258
                '<?php namespace Foo\Bar; use Foo; class Baz {}',
1259
                ['Foo\Bar\Baz'],
1260
            ],
1261
            [
1262
                '<?php namespace /*Comment*/ Foo\Bar; /** Foo */class /* Comment */ Baz {}',
1263
                ['Foo\Bar\Baz'],
1264
            ],
1265
            [
1266
                '
1267
<?php namespace
1268
/*Comment*/
1269
Foo\Bar
1270
;
1271
1272
/** Foo */
1273
class
1274
/* Comment */
1275
 Baz {}
1276
     ',
1277
                ['Foo\Bar\Baz'],
1278
            ],
1279
            [
1280
                '
1281
<?php namespace Foo\Bar; class Baz {
1282
    public static function someMethod(){
1283
        return self::class;
1284
    }
1285
}
1286
',
1287
                ['Foo\Bar\Baz'],
1288
            ],
1289
        ];
1290
    }
1291
1292
    /**
1293
     * @param string $type
1294
     * @param \ReflectionProperty $property
1295
     */
1296 View Code Duplication
    private function assertPhpDocVarType($type, \ReflectionProperty $property)
1297
    {
1298
        $docComment = $property->getDocComment();
1299
        $regex      = '/@var\s+([\S]+)$/m';
1300
1301
        self::assertRegExp($regex, $docComment);
1302
        self::assertEquals(1, preg_match($regex, $docComment, $matches));
1303
        self::assertEquals($type, $matches[1]);
1304
    }
1305
1306
    /**
1307
     * @param string $type
1308
     * @param \ReflectionMethod $method
1309
     */
1310 View Code Duplication
    private function assertPhpDocReturnType($type, \ReflectionMethod $method)
1311
    {
1312
        $docComment = $method->getDocComment();
1313
        $regex      = '/@return\s+([\S]+)(\s+.*)$/m';
1314
1315
        self::assertRegExp($regex, $docComment);
1316
        self::assertEquals(1, preg_match($regex, $docComment, $matches));
1317
        self::assertEquals($type, $matches[1]);
1318
    }
1319
1320
    /**
1321
     * @param string $type
1322
     * @param \ReflectionProperty $method
1323
     */
1324
    private function assertPhpDocParamType($type, \ReflectionMethod $method)
1325
    {
1326
        self::assertEquals(1, preg_match('/@param\s+([^\s]+)/', $method->getDocComment(), $matches));
1327
        self::assertEquals($type, $matches[1]);
1328
    }
1329
}
1330
1331
class EntityGeneratorAuthor {}
1332
class EntityGeneratorComment {}
1333