Completed
Pull Request — master (#1410)
by Ilya
10:40
created

lifecycleCallbacksGenerationProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 9.4285
cc 1
eloc 10
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM\Tools;
4
5
use Doctrine\Common\Annotations\AnnotationReader;
6
use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService;
7
use Doctrine\ORM\Mapping\ClassMetadataFactory;
8
use Doctrine\ORM\Mapping\ClassMetadataInfo;
9
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
10
use Doctrine\ORM\Tools\EntityGenerator;
11
use Doctrine\Tests\Models\DDC2372\DDC2372Admin;
12
use Doctrine\Tests\Models\DDC2372\DDC2372User;
13
use Doctrine\Tests\OrmTestCase;
14
15
class EntityGeneratorTest extends OrmTestCase
16
{
17
18
    /**
19
     * @var EntityGenerator
20
     */
21
    private $_generator;
22
    private $_tmpDir;
23
    private $_namespace;
24
25
    protected function setUp()
26
    {
27
        $this->_namespace = uniqid("doctrine_");
28
        $this->_tmpDir = \sys_get_temp_dir();
29
        \mkdir($this->_tmpDir . \DIRECTORY_SEPARATOR . $this->_namespace);
30
        $this->_generator = new EntityGenerator();
31
        $this->_generator->setAnnotationPrefix("");
32
        $this->_generator->setGenerateAnnotations(true);
33
        $this->_generator->setGenerateStubMethods(true);
34
        $this->_generator->setRegenerateEntityIfExists(false);
35
        $this->_generator->setUpdateEntityIfExists(true);
36
        $this->_generator->setFieldVisibility(EntityGenerator::FIELD_VISIBLE_PROTECTED);
37
    }
38
39
    protected function tearDown()
40
    {
41
        $ri = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->_tmpDir . '/' . $this->_namespace));
42
        foreach ($ri AS $file) {
43
            /* @var $file \SplFileInfo */
44
            if ($file->isFile()) {
45
                \unlink($file->getPathname());
46
            }
47
        }
48
        rmdir($this->_tmpDir . '/' . $this->_namespace);
49
    }
50
51
    /**
52
     * @param ClassMetadataInfo[] $embeddedClasses
53
     *
54
     * @return ClassMetadataInfo
55
     */
56
    public function generateBookEntityFixture(array $embeddedClasses = array())
57
    {
58
        $metadata = new ClassMetadataInfo($this->_namespace . '\EntityGeneratorBook');
59
        $metadata->namespace = $this->_namespace;
60
        $metadata->customRepositoryClassName = $this->_namespace  . '\EntityGeneratorBookRepository';
61
62
        $metadata->table['name'] = 'book';
63
        $metadata->table['uniqueConstraints']['name_uniq'] = array('columns' => array('name'));
64
        $metadata->table['indexes']['status_idx'] = array('columns' => array('status'));
65
        $metadata->mapField(array('fieldName' => 'name', 'type' => 'string'));
66
        $metadata->mapField(array('fieldName' => 'status', 'type' => 'string', 'options' => array('default' => 'published')));
67
        $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
68
        $metadata->mapOneToOne(array('fieldName' => 'author', 'targetEntity' => 'Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', 'mappedBy' => 'book'));
69
        $metadata->mapManyToMany(array(
70
            'fieldName' => 'comments',
71
            'targetEntity' => 'Doctrine\Tests\ORM\Tools\EntityGeneratorComment',
72
            'fetch' => ClassMetadataInfo::FETCH_EXTRA_LAZY,
73
            'joinTable' => array(
74
                'name' => 'book_comment',
75
                'joinColumns' => array(array('name' => 'book_id', 'referencedColumnName' => 'id')),
76
                'inverseJoinColumns' => array(array('name' => 'comment_id', 'referencedColumnName' => 'id')),
77
            ),
78
        ));
79
        $metadata->addLifecycleCallback('loading', 'postLoad');
80
        $metadata->addLifecycleCallback('willBeRemoved', 'preRemove');
81
82
        $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
83
84
        foreach ($embeddedClasses as $fieldName => $embeddedClass) {
85
            $this->mapNestedEmbedded($fieldName, $metadata, $embeddedClass);
86
            $this->mapEmbedded($fieldName, $metadata, $embeddedClass);
87
        }
88
89
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
90
91
        return $metadata;
92
    }
93
94
    private function generateEntityTypeFixture(array $field)
95
    {
96
        $metadata = new ClassMetadataInfo($this->_namespace . '\EntityType');
97
        $metadata->namespace = $this->_namespace;
98
99
        $metadata->table['name'] = 'entity_type';
100
        $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
101
        $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
102
103
        $name  = $field['fieldName'];
104
        $type  = $field['dbType'];
105
        $metadata->mapField(array('fieldName' => $name, 'type' => $type));
106
107
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
108
109
        return $metadata;
110
    }
111
112
    private function generateLifecycleEntityFixture(array $callbacks)
113
    {
114
        $metadata = new ClassMetadataInfo($this->_namespace . '\LifecycleEntity');
115
        $metadata->namespace = $this->_namespace;
116
117
        $metadata->table['name'] = 'lifecycle_entity';
118
        $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
119
        $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
120
121
        foreach ($callbacks as $method => $events) {
122
            foreach ($events as $event) {
123
                $metadata->addLifecycleCallback($method, $event);
124
            }
125
        }
126
127
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
128
129
        return $metadata;
130
    }
131
132
    /**
133
     * @return ClassMetadataInfo
134
     */
135
    private function generateIsbnEmbeddableFixture(array $embeddedClasses = array())
136
    {
137
        $metadata = new ClassMetadataInfo($this->_namespace . '\EntityGeneratorIsbn');
138
        $metadata->namespace = $this->_namespace;
139
        $metadata->isEmbeddedClass = true;
140
        $metadata->mapField(array('fieldName' => 'prefix', 'type' => 'integer'));
141
        $metadata->mapField(array('fieldName' => 'groupNumber', 'type' => 'integer'));
142
        $metadata->mapField(array('fieldName' => 'publisherNumber', 'type' => 'integer'));
143
        $metadata->mapField(array('fieldName' => 'titleNumber', 'type' => 'integer'));
144
        $metadata->mapField(array('fieldName' => 'checkDigit', 'type' => 'integer'));
145
146
        foreach ($embeddedClasses as $fieldName => $embeddedClass) {
147
            $this->mapEmbedded($fieldName, $metadata, $embeddedClass);
148
        }
149
150
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
151
152
        return $metadata;
153
    }
154
155
    /**
156
     * @return ClassMetadataInfo
157
     */
158
    private function generateTestEmbeddableFixture()
159
    {
160
        $metadata = new ClassMetadataInfo($this->_namespace . '\EntityGeneratorTestEmbeddable');
161
        $metadata->namespace = $this->_namespace;
162
        $metadata->isEmbeddedClass = true;
163
        $metadata->mapField(array('fieldName' => 'field1', 'type' => 'integer'));
164
        $metadata->mapField(array('fieldName' => 'field2', 'type' => 'integer', 'nullable' => true));
165
        $metadata->mapField(array('fieldName' => 'field3', 'type' => 'datetime'));
166
        $metadata->mapField(array('fieldName' => 'field4', 'type' => 'datetime', 'nullable' => true));
167
168
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
169
170
        return $metadata;
171
    }
172
173
    /**
174
     * @param string            $fieldName
175
     * @param ClassMetadataInfo $classMetadata
176
     * @param ClassMetadataInfo $embeddableMetadata
177
     * @param string|null       $columnPrefix
178
     */
179
    private function mapEmbedded(
180
        $fieldName,
181
        ClassMetadataInfo $classMetadata,
182
        ClassMetadataInfo $embeddableMetadata,
183
        $columnPrefix = false
184
    ) {
185
        $classMetadata->mapEmbedded(
186
            array('fieldName' => $fieldName, 'class' => $embeddableMetadata->name, 'columnPrefix' => $columnPrefix)
187
        );
188
    }
189
190
    /**
191
     * @param string            $fieldName
192
     * @param ClassMetadataInfo $classMetadata
193
     * @param ClassMetadataInfo $embeddableMetadata
194
     */
195
    private function mapNestedEmbedded(
196
        $fieldName,
197
        ClassMetadataInfo $classMetadata,
198
        ClassMetadataInfo $embeddableMetadata
199
    ) {
200
        foreach ($embeddableMetadata->embeddedClasses as $property => $embeddableClass) {
201
            $classMetadata->mapEmbedded(array(
202
                'fieldName' => $fieldName . '.' . $property,
203
                'class' => $embeddableClass['class'],
204
                'columnPrefix' => $embeddableClass['columnPrefix'],
205
                'declaredField' => $embeddableClass['declaredField']
206
                        ? $fieldName . '.' . $embeddableClass['declaredField']
207
                        : $fieldName,
208
                'originalField' => $embeddableClass['originalField'] ?: $property,
209
            ));
210
        }
211
    }
212
213
    /**
214
     * @param ClassMetadataInfo $metadata
215
     */
216
    private function loadEntityClass(ClassMetadataInfo $metadata)
217
    {
218
        $className = basename(str_replace('\\', '/', $metadata->name));
219
        $path = $this->_tmpDir . '/' . $this->_namespace . '/' . $className . '.php';
220
221
        $this->assertFileExists($path);
222
223
        require_once $path;
224
    }
225
226
    /**
227
     * @param  ClassMetadataInfo $metadata
228
     *
229
     * @return mixed An instance of the given metadata's class.
230
     */
231
    public function newInstance($metadata)
232
    {
233
        $this->loadEntityClass($metadata);
234
235
        return new $metadata->name;
236
    }
237
238
    public function testGeneratedEntityClass()
239
    {
240
        $testMetadata = $this->generateTestEmbeddableFixture();
241
        $isbnMetadata = $this->generateIsbnEmbeddableFixture(array('test' => $testMetadata));
242
        $metadata = $this->generateBookEntityFixture(array('isbn' => $isbnMetadata));
243
244
        $book = $this->newInstance($metadata);
245
        $this->assertTrue(class_exists($metadata->name), "Class does not exist.");
246
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', '__construct'), "EntityGeneratorBook::__construct() missing.");
247
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getId'), "EntityGeneratorBook::getId() missing.");
248
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setName'), "EntityGeneratorBook::setName() missing.");
249
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getName'), "EntityGeneratorBook::getName() missing.");
250
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setStatus'), "EntityGeneratorBook::setStatus() missing.");
251
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getStatus'), "EntityGeneratorBook::getStatus() missing.");
252
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setAuthor'), "EntityGeneratorBook::setAuthor() missing.");
253
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getAuthor'), "EntityGeneratorBook::getAuthor() missing.");
254
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getComments'), "EntityGeneratorBook::getComments() missing.");
255
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'addComment'), "EntityGeneratorBook::addComment() missing.");
256
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'removeComment'), "EntityGeneratorBook::removeComment() missing.");
257
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setIsbn'), "EntityGeneratorBook::setIsbn() missing.");
258
        $this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getIsbn'), "EntityGeneratorBook::getIsbn() missing.");
259
260
        $reflClass = new \ReflectionClass($metadata->name);
261
262
        $this->assertCount(6, $reflClass->getProperties());
263
        $this->assertCount(15, $reflClass->getMethods());
264
265
        $this->assertEquals('published', $book->getStatus());
266
267
        $book->setName('Jonathan H. Wage');
268
        $this->assertEquals('Jonathan H. Wage', $book->getName());
269
270
        $reflMethod = new \ReflectionMethod($metadata->name, 'addComment');
271
        $addCommentParameters = $reflMethod->getParameters();
272
        $this->assertEquals('comment', $addCommentParameters[0]->getName());
273
274
        $reflMethod = new \ReflectionMethod($metadata->name, 'removeComment');
275
        $removeCommentParameters = $reflMethod->getParameters();
276
        $this->assertEquals('comment', $removeCommentParameters[0]->getName());
277
278
        $author = new EntityGeneratorAuthor();
279
        $book->setAuthor($author);
280
        $this->assertEquals($author, $book->getAuthor());
281
282
        $comment = new EntityGeneratorComment();
283
        $this->assertInstanceOf($metadata->name, $book->addComment($comment));
284
        $this->assertInstanceOf('Doctrine\Common\Collections\ArrayCollection', $book->getComments());
285
        $this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array($comment)), $book->getComments());
286
        $this->assertInternalType('boolean', $book->removeComment($comment));
287
        $this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array()), $book->getComments());
288
289
        $this->newInstance($isbnMetadata);
290
        $isbn = new $isbnMetadata->name();
291
292
        $book->setIsbn($isbn);
293
        $this->assertSame($isbn, $book->getIsbn());
294
295
        $reflMethod = new \ReflectionMethod($metadata->name, 'setIsbn');
296
        $reflParameters = $reflMethod->getParameters();
297
        $this->assertEquals($isbnMetadata->name, $reflParameters[0]->getClass()->name);
298
    }
299
300
    /**
301
     * @param array $callbacks
302
     *
303
     * @dataProvider lifecycleCallbacksGenerationProvider
304
     */
305
    public function testLifecycleCallbacksGeneration($callbacks)
306
    {
307
        $metadata = $this->generateLifecycleEntityFixture($callbacks);
308
        $this->loadEntityClass($metadata);
309
310
        $this->assertTrue(class_exists($metadata->name), "Class does not exist.");
311
312
        $reflClass = new \ReflectionClass($metadata->name);
313
314
        foreach ($callbacks as $method => $events) {
315
            $events = array_unique($events);
316
317
            $this->assertTrue(method_exists($metadata->name, $method), sprintf("%s::%s() missing.", $metadata->name, $method));
318
319
            $reflMethod = $reflClass->getMethod($method);
320
            $docComment = $reflMethod->getDocComment();
321
322
            preg_match_all('#@(.*?)\n#s', $docComment, $annotationMatches);
323
            $annotations = array_map('trim', $annotationMatches[0]);
324
325
            $this->assertCount(count($events), $annotations);
326
327
            foreach ($events as $event) {
328
                $this->assertContains(sprintf('@%s', $event), $annotations);
329
            }
330
        }
331
    }
332
333
    public function testEntityUpdatingWorks()
334
    {
335
        $metadata = $this->generateBookEntityFixture(array('isbn' => $this->generateIsbnEmbeddableFixture()));
336
337
        $metadata->mapField(array('fieldName' => 'test', 'type' => 'string'));
338
339
        $testEmbeddableMetadata = $this->generateTestEmbeddableFixture();
340
        $this->mapEmbedded('testEmbedded', $metadata, $testEmbeddableMetadata);
341
342
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
343
344
        $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/EntityGeneratorBook.php~");
345
346
        $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...
347
        $reflClass = new \ReflectionClass($metadata->name);
348
349
        $this->assertTrue($reflClass->hasProperty('name'), "Regenerating keeps property 'name'.");
350
        $this->assertTrue($reflClass->hasProperty('status'), "Regenerating keeps property 'status'.");
351
        $this->assertTrue($reflClass->hasProperty('id'), "Regenerating keeps property 'id'.");
352
        $this->assertTrue($reflClass->hasProperty('isbn'), "Regenerating keeps property 'isbn'.");
353
354
        $this->assertTrue($reflClass->hasProperty('test'), "Check for property test failed.");
355
        $this->assertTrue($reflClass->getProperty('test')->isProtected(), "Check for protected property test failed.");
356
        $this->assertTrue($reflClass->hasProperty('testEmbedded'), "Check for property testEmbedded failed.");
357
        $this->assertTrue($reflClass->getProperty('testEmbedded')->isProtected(), "Check for protected property testEmbedded failed.");
358
        $this->assertTrue($reflClass->hasMethod('getTest'), "Check for method 'getTest' failed.");
359
        $this->assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed.");
360
        $this->assertTrue($reflClass->hasMethod('setTest'), "Check for method 'setTest' failed.");
361
        $this->assertTrue($reflClass->getMethod('setTest')->isPublic(), "Check for public visibility of method 'setTest' failed.");
362
        $this->assertTrue($reflClass->hasMethod('getTestEmbedded'), "Check for method 'getTestEmbedded' failed.");
363
        $this->assertTrue(
364
            $reflClass->getMethod('getTestEmbedded')->isPublic(),
365
            "Check for public visibility of method 'getTestEmbedded' failed."
366
        );
367
        $this->assertTrue($reflClass->hasMethod('setTestEmbedded'), "Check for method 'setTestEmbedded' failed.");
368
        $this->assertTrue(
369
            $reflClass->getMethod('setTestEmbedded')->isPublic(),
370
            "Check for public visibility of method 'setTestEmbedded' failed."
371
        );
372
    }
373
374
    /**
375
     * @group DDC-3152
376
     */
377
    public function testDoesNotRegenerateExistingMethodsWithDifferentCase()
378
    {
379
        $metadata = $this->generateBookEntityFixture(array('isbn' => $this->generateIsbnEmbeddableFixture()));
380
381
        // Workaround to change existing fields case (just to simulate the use case)
382
        $metadata->fieldMappings['status']['fieldName'] = 'STATUS';
383
        $metadata->embeddedClasses['ISBN'] = $metadata->embeddedClasses['isbn'];
384
        unset($metadata->embeddedClasses['isbn']);
385
386
        // Should not throw a PHP fatal error
387
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
388
389
        $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/EntityGeneratorBook.php~");
390
391
        $this->newInstance($metadata);
392
        $reflClass = new \ReflectionClass($metadata->name);
393
394
        $this->assertTrue($reflClass->hasProperty('status'));
395
        $this->assertTrue($reflClass->hasProperty('STATUS'));
396
        $this->assertTrue($reflClass->hasProperty('isbn'));
397
        $this->assertTrue($reflClass->hasProperty('ISBN'));
398
        $this->assertTrue($reflClass->hasMethod('getStatus'));
399
        $this->assertTrue($reflClass->hasMethod('setStatus'));
400
        $this->assertTrue($reflClass->hasMethod('getIsbn'));
401
        $this->assertTrue($reflClass->hasMethod('setIsbn'));
402
    }
403
404
    /**
405
     * @group DDC-2121
406
     */
407
    public function testMethodDocBlockShouldStartWithBackSlash()
408
    {
409
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
410
        $metadata = $this->generateBookEntityFixture(array('isbn' => $embeddedMetadata));
411
        $book     = $this->newInstance($metadata);
412
413
        $this->assertPhpDocVarType('\Doctrine\Common\Collections\Collection', new \ReflectionProperty($book, 'comments'));
414
        $this->assertPhpDocReturnType('\Doctrine\Common\Collections\Collection', new \ReflectionMethod($book, 'getComments'));
415
        $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'addComment'));
416
        $this->assertPhpDocReturnType('EntityGeneratorBook', new \ReflectionMethod($book, 'addComment'));
417
        $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new \ReflectionMethod($book, 'removeComment'));
418
        $this->assertPhpDocReturnType('boolean', new \ReflectionMethod($book, 'removeComment'));
419
420
        $this->assertPhpDocVarType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new \ReflectionProperty($book, 'author'));
421
        $this->assertPhpDocReturnType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new \ReflectionMethod($book, 'getAuthor'));
422
        $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new \ReflectionMethod($book, 'setAuthor'));
423
424
        $expectedClassName = '\\' . $embeddedMetadata->name;
425
        $this->assertPhpDocVarType($expectedClassName, new \ReflectionProperty($book, 'isbn'));
426
        $this->assertPhpDocReturnType($expectedClassName, new \ReflectionMethod($book, 'getIsbn'));
427
        $this->assertPhpDocParamType($expectedClassName, new \ReflectionMethod($book, 'setIsbn'));
428
    }
429
430
    public function testEntityExtendsStdClass()
431
    {
432
        $this->_generator->setClassToExtend('stdClass');
433
        $metadata = $this->generateBookEntityFixture();
434
435
        $book = $this->newInstance($metadata);
436
        $this->assertInstanceOf('stdClass', $book);
437
438
        $metadata = $this->generateIsbnEmbeddableFixture();
439
        $isbn = $this->newInstance($metadata);
440
        $this->assertInstanceOf('stdClass', $isbn);
441
    }
442
443
    public function testLifecycleCallbacks()
444
    {
445
        $metadata = $this->generateBookEntityFixture();
446
447
        $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...
448
        $reflClass = new \ReflectionClass($metadata->name);
449
450
        $this->assertTrue($reflClass->hasMethod('loading'), "Check for postLoad lifecycle callback.");
451
        $this->assertTrue($reflClass->hasMethod('willBeRemoved'), "Check for preRemove lifecycle callback.");
452
    }
453
454
    public function testLoadMetadata()
455
    {
456
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
457
        $metadata = $this->generateBookEntityFixture(array('isbn' => $embeddedMetadata));
458
459
        $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...
460
461
        $reflectionService = new RuntimeReflectionService();
462
463
        $cm = new ClassMetadataInfo($metadata->name);
464
        $cm->initializeReflection($reflectionService);
465
466
        $driver = $this->createAnnotationDriver();
467
        $driver->loadMetadataForClass($cm->name, $cm);
468
469
        $this->assertEquals($cm->columnNames, $metadata->columnNames);
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
470
        $this->assertEquals($cm->getTableName(), $metadata->getTableName());
471
        $this->assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
472
        $this->assertEquals($cm->identifier, $metadata->identifier);
473
        $this->assertEquals($cm->idGenerator, $metadata->idGenerator);
474
        $this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName);
475
        $this->assertEquals($cm->embeddedClasses, $metadata->embeddedClasses);
476
        $this->assertEquals($cm->isEmbeddedClass, $metadata->isEmbeddedClass);
477
478
        $this->assertEquals(ClassMetadataInfo::FETCH_EXTRA_LAZY, $cm->associationMappings['comments']['fetch']);
479
480
        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code introduced by
$isbn 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...
481
482
        $cm = new ClassMetadataInfo($embeddedMetadata->name);
483
        $cm->initializeReflection($reflectionService);
484
485
        $driver->loadMetadataForClass($cm->name, $cm);
486
487
        $this->assertEquals($cm->columnNames, $embeddedMetadata->columnNames);
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
488
        $this->assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
489
        $this->assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
490
    }
491
492
    public function testLoadPrefixedMetadata()
493
    {
494
        $this->_generator->setAnnotationPrefix('ORM\\');
495
        $embeddedMetadata = $this->generateIsbnEmbeddableFixture();
496
        $metadata = $this->generateBookEntityFixture(array('isbn' => $embeddedMetadata));
497
498
        $reader = new AnnotationReader();
499
        $driver = new AnnotationDriver($reader, array());
500
501
        $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...
502
503
        $reflectionService = new RuntimeReflectionService();
504
505
        $cm = new ClassMetadataInfo($metadata->name);
506
        $cm->initializeReflection($reflectionService);
507
508
        $driver->loadMetadataForClass($cm->name, $cm);
509
510
        $this->assertEquals($cm->columnNames, $metadata->columnNames);
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
511
        $this->assertEquals($cm->getTableName(), $metadata->getTableName());
512
        $this->assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks);
513
        $this->assertEquals($cm->identifier, $metadata->identifier);
514
        $this->assertEquals($cm->idGenerator, $metadata->idGenerator);
515
        $this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName);
516
517
        $isbn = $this->newInstance($embeddedMetadata);
0 ignored issues
show
Unused Code introduced by
$isbn 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...
518
519
        $cm = new ClassMetadataInfo($embeddedMetadata->name);
520
        $cm->initializeReflection($reflectionService);
521
522
        $driver->loadMetadataForClass($cm->name, $cm);
523
524
        $this->assertEquals($cm->columnNames, $embeddedMetadata->columnNames);
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
525
        $this->assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses);
526
        $this->assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass);
527
    }
528
529
    /**
530
     * @group DDC-3272
531
     */
532
    public function testMappedSuperclassAnnotationGeneration()
533
    {
534
        $metadata                     = new ClassMetadataInfo($this->_namespace . '\EntityGeneratorBook');
535
        $metadata->namespace          = $this->_namespace;
536
        $metadata->isMappedSuperclass = true;
537
538
        $this->_generator->setAnnotationPrefix('ORM\\');
539
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
540
        $this->newInstance($metadata); // force instantiation (causes autoloading to kick in)
541
542
        $driver = new AnnotationDriver(new AnnotationReader(), array());
543
        $cm     = new ClassMetadataInfo($metadata->name);
544
545
        $cm->initializeReflection(new RuntimeReflectionService);
546
        $driver->loadMetadataForClass($cm->name, $cm);
547
548
        $this->assertTrue($cm->isMappedSuperclass);
549
    }
550
551
    /**
552
     * @dataProvider getParseTokensInEntityFileData
553
     */
554
    public function testParseTokensInEntityFile($php, $classes)
555
    {
556
        $r = new \ReflectionObject($this->_generator);
557
        $m = $r->getMethod('parseTokensInEntityFile');
558
        $m->setAccessible(true);
559
560
        $p = $r->getProperty('staticReflection');
561
        $p->setAccessible(true);
562
563
        $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...
564
        $this->assertEquals($classes, array_keys($p->getValue($this->_generator)));
565
    }
566
567
    /**
568
     * @group DDC-1784
569
     */
570
    public function testGenerateEntityWithSequenceGenerator()
571
    {
572
        $metadata               = new ClassMetadataInfo($this->_namespace . '\DDC1784Entity');
573
        $metadata->namespace    = $this->_namespace;
574
        $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
575
        $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE);
576
        $metadata->setSequenceGeneratorDefinition(array(
577
            'sequenceName'      => 'DDC1784_ID_SEQ',
578
            'allocationSize'    => 1,
579
            'initialValue'      => 2
580
        ));
581
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
582
583
        $filename = $this->_tmpDir . DIRECTORY_SEPARATOR
584
                  . $this->_namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php';
585
586
        $this->assertFileExists($filename);
587
        require_once $filename;
588
589
590
        $reflection = new \ReflectionProperty($metadata->name, 'id');
591
        $docComment = $reflection->getDocComment();
592
593
        $this->assertContains('@Id', $docComment);
594
        $this->assertContains('@Column(name="id", type="integer")', $docComment);
595
        $this->assertContains('@GeneratedValue(strategy="SEQUENCE")', $docComment);
596
        $this->assertContains('@SequenceGenerator(sequenceName="DDC1784_ID_SEQ", allocationSize=1, initialValue=2)', $docComment);
597
    }
598
599
    /**
600
     * @group DDC-2079
601
     */
602
    public function testGenerateEntityWithMultipleInverseJoinColumns()
603
    {
604
        $metadata               = new ClassMetadataInfo($this->_namespace . '\DDC2079Entity');
605
        $metadata->namespace    = $this->_namespace;
606
        $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
607
        $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE);
608
        $metadata->mapManyToMany(array(
609
            'fieldName'     => 'centroCustos',
610
            'targetEntity'  => 'DDC2079CentroCusto',
611
            'joinTable'     => array(
612
                'name'                  => 'unidade_centro_custo',
613
                'joinColumns'           => array(
614
                    array('name' => 'idorcamento',      'referencedColumnName' => 'idorcamento'),
615
                    array('name' => 'idunidade',        'referencedColumnName' => 'idunidade')
616
                ),
617
                'inverseJoinColumns'    => array(
618
                    array('name' => 'idcentrocusto',    'referencedColumnName' => 'idcentrocusto'),
619
                    array('name' => 'idpais',           'referencedColumnName' => 'idpais'),
620
                ),
621
            ),
622
        ));
623
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
624
625
        $filename = $this->_tmpDir . DIRECTORY_SEPARATOR
626
            . $this->_namespace . DIRECTORY_SEPARATOR . 'DDC2079Entity.php';
627
628
        $this->assertFileExists($filename);
629
        require_once $filename;
630
631
        $property   = new \ReflectionProperty($metadata->name, 'centroCustos');
632
        $docComment = $property->getDocComment();
633
634
        //joinColumns
635
        $this->assertContains('@JoinColumn(name="idorcamento", referencedColumnName="idorcamento"),', $docComment);
636
        $this->assertContains('@JoinColumn(name="idunidade", referencedColumnName="idunidade")', $docComment);
637
        //inverseJoinColumns
638
        $this->assertContains('@JoinColumn(name="idcentrocusto", referencedColumnName="idcentrocusto"),', $docComment);
639
        $this->assertContains('@JoinColumn(name="idpais", referencedColumnName="idpais")', $docComment);
640
641
    }
642
643
     /**
644
     * @group DDC-2172
645
     */
646
    public function testGetInheritanceTypeString()
647
    {
648
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadataInfo');
649
        $method     = new \ReflectionMethod($this->_generator, 'getInheritanceTypeString');
650
        $constants  = $reflection->getConstants();
651
        $pattern    = '/^INHERITANCE_TYPE_/';
652
653
        $method->setAccessible(true);
654
655
        foreach ($constants as $name => $value) {
656
            if( ! preg_match($pattern, $name)) {
657
                continue;
658
            }
659
660
            $expected = preg_replace($pattern, '', $name);
661
            $actual   = $method->invoke($this->_generator, $value);
662
663
            $this->assertEquals($expected, $actual);
664
        }
665
666
        $this->setExpectedException('\InvalidArgumentException', 'Invalid provided InheritanceType: INVALID');
667
        $method->invoke($this->_generator, 'INVALID');
668
    }
669
670
    /**
671
    * @group DDC-2172
672
    */
673
    public function testGetChangeTrackingPolicyString()
674
    {
675
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata');
676
        $method     = new \ReflectionMethod($this->_generator, 'getChangeTrackingPolicyString');
677
        $constants  = $reflection->getConstants();
678
        $pattern    = '/^CHANGETRACKING_/';
679
680
        $method->setAccessible(true);
681
682
        foreach ($constants as $name => $value) {
683
            if( ! preg_match($pattern, $name)) {
684
                continue;
685
            }
686
687
            $expected = preg_replace($pattern, '', $name);
688
            $actual   = $method->invoke($this->_generator, $value);
689
690
            $this->assertEquals($expected, $actual);
691
        }
692
693
        $this->setExpectedException('\InvalidArgumentException', 'Invalid provided ChangeTrackingPolicy: INVALID');
694
        $method->invoke($this->_generator, 'INVALID');
695
    }
696
697
    /**
698
     * @group DDC-2172
699
     */
700
    public function testGetIdGeneratorTypeString()
701
    {
702
        $reflection = new \ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadataInfo');
703
        $method     = new \ReflectionMethod($this->_generator, 'getIdGeneratorTypeString');
704
        $constants  = $reflection->getConstants();
705
        $pattern    = '/^GENERATOR_TYPE_/';
706
707
        $method->setAccessible(true);
708
709
        foreach ($constants as $name => $value) {
710
            if( ! preg_match($pattern, $name)) {
711
                continue;
712
            }
713
714
            $expected = preg_replace($pattern, '', $name);
715
            $actual   = $method->invoke($this->_generator, $value);
716
717
            $this->assertEquals($expected, $actual);
718
        }
719
720
        $this->setExpectedException('\InvalidArgumentException', 'Invalid provided IdGeneratorType: INVALID');
721
        $method->invoke($this->_generator, 'INVALID');
722
    }
723
724
    /**
725
     * @dataProvider getEntityTypeAliasDataProvider
726
     *
727
     * @group DDC-1694
728
     */
729
    public function testEntityTypeAlias(array $field)
730
    {
731
        $metadata   = $this->generateEntityTypeFixture($field);
732
        $path       = $this->_tmpDir . '/'. $this->_namespace . '/EntityType.php';
733
734
        $this->assertFileExists($path);
735
        require_once $path;
736
737
        $entity     = new $metadata->name;
738
        $reflClass  = new \ReflectionClass($metadata->name);
739
740
        $type   = $field['phpType'];
741
        $name   = $field['fieldName'];
742
        $value  = $field['value'];
743
        $getter = "get" . ucfirst($name);
744
        $setter = "set" . ucfirst($name);
745
746
        $this->assertPhpDocVarType($type, $reflClass->getProperty($name));
747
        $this->assertPhpDocParamType($type, $reflClass->getMethod($setter));
748
        $this->assertPhpDocReturnType($type, $reflClass->getMethod($getter));
749
750
        $this->assertSame($entity, $entity->{$setter}($value));
751
        $this->assertEquals($value, $entity->{$getter}());
752
    }
753
754
    /**
755
     * @group DDC-2372
756
     */
757
    public function testTraitPropertiesAndMethodsAreNotDuplicated()
758
    {
759
        $cmf = new ClassMetadataFactory();
760
        $em = $this->_getTestEntityManager();
761
        $cmf->setEntityManager($em);
762
763
        $user = new DDC2372User();
764
        $metadata = $cmf->getMetadataFor(get_class($user));
765
        $metadata->name = $this->_namespace . "\DDC2372User";
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
766
        $metadata->namespace = $this->_namespace;
0 ignored issues
show
Bug introduced by
Accessing namespace on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
767
768
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
769
770
        $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php");
771
        require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php";
772
773
        $reflClass = new \ReflectionClass($metadata->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
774
775
        $this->assertSame($reflClass->hasProperty('address'), false);
776
        $this->assertSame($reflClass->hasMethod('setAddress'), false);
777
        $this->assertSame($reflClass->hasMethod('getAddress'), false);
778
    }
779
780
    /**
781
     * @group DDC-2372
782
     */
783
    public function testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses()
784
    {
785
        $cmf = new ClassMetadataFactory();
786
        $em = $this->_getTestEntityManager();
787
        $cmf->setEntityManager($em);
788
789
        $user = new DDC2372Admin();
790
        $metadata = $cmf->getMetadataFor(get_class($user));
791
        $metadata->name = $this->_namespace . "\DDC2372Admin";
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
792
        $metadata->namespace = $this->_namespace;
0 ignored issues
show
Bug introduced by
Accessing namespace on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
793
794
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
795
796
        $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372Admin.php");
797
        require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372Admin.php";
798
799
        $reflClass = new \ReflectionClass($metadata->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
800
801
        $this->assertSame($reflClass->hasProperty('address'), false);
802
        $this->assertSame($reflClass->hasMethod('setAddress'), false);
803
        $this->assertSame($reflClass->hasMethod('getAddress'), false);
804
    }
805
806
    /**
807
     * @group DDC-1590
808
     */
809
    public function testMethodsAndPropertiesAreNotDuplicatedInChildClasses()
810
    {
811
        $cmf    = new ClassMetadataFactory();
812
        $em     = $this->_getTestEntityManager();
813
814
        $cmf->setEntityManager($em);
815
816
        $ns     = $this->_namespace;
817
        $nsdir  = $this->_tmpDir . '/' . $ns;
818
819
        $content = str_replace(
820
            'namespace Doctrine\Tests\Models\DDC1590',
821
            'namespace ' . $ns,
822
            file_get_contents(__DIR__ . '/../../Models/DDC1590/DDC1590User.php')
823
        );
824
825
        $fname = $nsdir . "/DDC1590User.php";
826
        file_put_contents($fname, $content);
827
        require $fname;
828
829
830
        $metadata = $cmf->getMetadataFor($ns . '\DDC1590User');
831
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
832
833
        // class DDC1590User extends DDC1590Entity { ... }
834
        $source = file_get_contents($fname);
835
836
        // class _DDC1590User extends DDC1590Entity { ... }
837
        $source2    = str_replace('class DDC1590User', 'class _DDC1590User', $source);
838
        $fname2     = $nsdir . "/_DDC1590User.php";
839
        file_put_contents($fname2, $source2);
840
        require $fname2;
841
842
        // class __DDC1590User { ... }
843
        $source3    = str_replace('class DDC1590User extends DDC1590Entity', 'class __DDC1590User', $source);
844
        $fname3     = $nsdir . "/__DDC1590User.php";
845
        file_put_contents($fname3, $source3);
846
        require $fname3;
847
848
849
        // class _DDC1590User extends DDC1590Entity { ... }
850
        $rc2 = new \ReflectionClass($ns.'\_DDC1590User');
851
852
        $this->assertTrue($rc2->hasProperty('name'));
853
        $this->assertTrue($rc2->hasProperty('id'));
854
        $this->assertTrue($rc2->hasProperty('created_at'));
855
856
        $this->assertTrue($rc2->hasMethod('getName'));
857
        $this->assertTrue($rc2->hasMethod('setName'));
858
        $this->assertTrue($rc2->hasMethod('getId'));
859
        $this->assertFalse($rc2->hasMethod('setId'));
860
        $this->assertTrue($rc2->hasMethod('getCreatedAt'));
861
        $this->assertTrue($rc2->hasMethod('setCreatedAt'));
862
863
864
        // class __DDC1590User { ... }
865
        $rc3 = new \ReflectionClass($ns.'\__DDC1590User');
866
867
        $this->assertTrue($rc3->hasProperty('name'));
868
        $this->assertFalse($rc3->hasProperty('id'));
869
        $this->assertFalse($rc3->hasProperty('created_at'));
870
871
        $this->assertTrue($rc3->hasMethod('getName'));
872
        $this->assertTrue($rc3->hasMethod('setName'));
873
        $this->assertFalse($rc3->hasMethod('getId'));
874
        $this->assertFalse($rc3->hasMethod('setId'));
875
        $this->assertFalse($rc3->hasMethod('getCreatedAt'));
876
        $this->assertFalse($rc3->hasMethod('setCreatedAt'));
877
    }
878
879
    /**
880
     * @group DDC-3304
881
     */
882
    public function testGeneratedMutableEmbeddablesClass()
883
    {
884
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
885
        $metadata = $this->generateIsbnEmbeddableFixture(array('test' => $embeddedMetadata));
886
887
        $isbn = $this->newInstance($metadata);
888
889
        $this->assertTrue(class_exists($metadata->name), "Class does not exist.");
890
        $this->assertFalse(method_exists($metadata->name, '__construct'), "EntityGeneratorIsbn::__construct present.");
891
        $this->assertTrue(method_exists($metadata->name, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
892
        $this->assertTrue(method_exists($metadata->name, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() missing.");
893
        $this->assertTrue(method_exists($metadata->name, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
894
        $this->assertTrue(method_exists($metadata->name, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() missing.");
895
        $this->assertTrue(method_exists($metadata->name, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
896
        $this->assertTrue(method_exists($metadata->name, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() missing.");
897
        $this->assertTrue(method_exists($metadata->name, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
898
        $this->assertTrue(method_exists($metadata->name, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() missing.");
899
        $this->assertTrue(method_exists($metadata->name, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
900
        $this->assertTrue(method_exists($metadata->name, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() missing.");
901
        $this->assertTrue(method_exists($metadata->name, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
902
        $this->assertTrue(method_exists($metadata->name, 'setTest'), "EntityGeneratorIsbn::setTest() missing.");
903
904
        $isbn->setPrefix(978);
905
        $this->assertSame(978, $isbn->getPrefix());
906
907
        $this->newInstance($embeddedMetadata);
908
        $test = new $embeddedMetadata->name();
909
910
        $isbn->setTest($test);
911
        $this->assertSame($test, $isbn->getTest());
912
913
        $reflMethod = new \ReflectionMethod($metadata->name, 'setTest');
914
        $reflParameters = $reflMethod->getParameters();
915
        $this->assertEquals($embeddedMetadata->name, $reflParameters[0]->getClass()->name);
916
    }
917
918
    /**
919
     * @group DDC-3304
920
     */
921
    public function testGeneratedImmutableEmbeddablesClass()
922
    {
923
        $this->_generator->setEmbeddablesImmutable(true);
924
        $embeddedMetadata = $this->generateTestEmbeddableFixture();
925
        $metadata = $this->generateIsbnEmbeddableFixture(array('test' => $embeddedMetadata));
926
927
        $this->loadEntityClass($embeddedMetadata);
928
        $this->loadEntityClass($metadata);
929
930
        $this->assertTrue(class_exists($metadata->name), "Class does not exist.");
931
        $this->assertTrue(method_exists($metadata->name, '__construct'), "EntityGeneratorIsbn::__construct missing.");
932
        $this->assertTrue(method_exists($metadata->name, 'getPrefix'), "EntityGeneratorIsbn::getPrefix() missing.");
933
        $this->assertFalse(method_exists($metadata->name, 'setPrefix'), "EntityGeneratorIsbn::setPrefix() present.");
934
        $this->assertTrue(method_exists($metadata->name, 'getGroupNumber'), "EntityGeneratorIsbn::getGroupNumber() missing.");
935
        $this->assertFalse(method_exists($metadata->name, 'setGroupNumber'), "EntityGeneratorIsbn::setGroupNumber() present.");
936
        $this->assertTrue(method_exists($metadata->name, 'getPublisherNumber'), "EntityGeneratorIsbn::getPublisherNumber() missing.");
937
        $this->assertFalse(method_exists($metadata->name, 'setPublisherNumber'), "EntityGeneratorIsbn::setPublisherNumber() present.");
938
        $this->assertTrue(method_exists($metadata->name, 'getTitleNumber'), "EntityGeneratorIsbn::getTitleNumber() missing.");
939
        $this->assertFalse(method_exists($metadata->name, 'setTitleNumber'), "EntityGeneratorIsbn::setTitleNumber() present.");
940
        $this->assertTrue(method_exists($metadata->name, 'getCheckDigit'), "EntityGeneratorIsbn::getCheckDigit() missing.");
941
        $this->assertFalse(method_exists($metadata->name, 'setCheckDigit'), "EntityGeneratorIsbn::setCheckDigit() present.");
942
        $this->assertTrue(method_exists($metadata->name, 'getTest'), "EntityGeneratorIsbn::getTest() missing.");
943
        $this->assertFalse(method_exists($metadata->name, 'setTest'), "EntityGeneratorIsbn::setTest() present.");
944
945
        $test = new $embeddedMetadata->name(1, new \DateTime());
946
        $isbn = new $metadata->name($test, 978, 3, 12, 732320, 83);
947
948
        $reflMethod = new \ReflectionMethod($isbn, '__construct');
949
        $reflParameters = $reflMethod->getParameters();
950
951
        $this->assertCount(6, $reflParameters);
952
953
        $this->assertSame($embeddedMetadata->name, $reflParameters[0]->getClass()->name);
954
        $this->assertSame('test', $reflParameters[0]->getName());
955
        $this->assertFalse($reflParameters[0]->isOptional());
956
957
        $this->assertSame('prefix', $reflParameters[1]->getName());
958
        $this->assertFalse($reflParameters[1]->isOptional());
959
960
        $this->assertSame('groupNumber', $reflParameters[2]->getName());
961
        $this->assertFalse($reflParameters[2]->isOptional());
962
963
        $this->assertSame('publisherNumber', $reflParameters[3]->getName());
964
        $this->assertFalse($reflParameters[3]->isOptional());
965
966
        $this->assertSame('titleNumber', $reflParameters[4]->getName());
967
        $this->assertFalse($reflParameters[4]->isOptional());
968
969
        $this->assertSame('checkDigit', $reflParameters[5]->getName());
970
        $this->assertFalse($reflParameters[5]->isOptional());
971
972
        $reflMethod = new \ReflectionMethod($test, '__construct');
973
        $reflParameters = $reflMethod->getParameters();
974
975
        $this->assertCount(4, $reflParameters);
976
977
        $this->assertSame('field1', $reflParameters[0]->getName());
978
        $this->assertFalse($reflParameters[0]->isOptional());
979
980
        $this->assertSame('DateTime', $reflParameters[1]->getClass()->name);
981
        $this->assertSame('field3', $reflParameters[1]->getName());
982
        $this->assertFalse($reflParameters[1]->isOptional());
983
984
        $this->assertSame('field2', $reflParameters[2]->getName());
985
        $this->assertTrue($reflParameters[2]->isOptional());
986
987
        $this->assertSame('DateTime', $reflParameters[3]->getClass()->name);
988
        $this->assertSame('field4', $reflParameters[3]->getName());
989
        $this->assertTrue($reflParameters[3]->isOptional());
990
    }
991
992
    public function testRegenerateEntityClass()
993
    {
994
        $metadata = $this->generateBookEntityFixture();
995
        $this->loadEntityClass($metadata);
996
997
        $className = basename(str_replace('\\', '/', $metadata->name));
998
        $path = $this->_tmpDir . '/' . $this->_namespace . '/' . $className . '.php';
999
        $classTest = file_get_contents($path);
1000
1001
        $this->_generator->setRegenerateEntityIfExists(true);
1002
        $this->_generator->setBackupExisting(false);
1003
1004
        $this->_generator->writeEntityClass($metadata, $this->_tmpDir);
1005
        $classNew = file_get_contents($path);
1006
1007
        $this->assertSame($classTest,$classNew);
1008
    }
1009
1010
    public function lifecycleCallbacksGenerationProvider()
1011
    {
1012
        return [
1013
            'with callbacks' => [
1014
                'callbacks' => [
1015
                    'updatedAt' => ['PreUpdate'],
1016
                    'createdAt' => ['PreUpdate', 'PrePersist'],
1017
                ],
1018
            ],
1019
            'with duplicated callbacks' => [
1020
                'callbacks' => [
1021
                    'updatedAt' => ['PreUpdate'],
1022
                    'createdAt' => ['PreUpdate', 'PrePersist', 'PrePersist'],
1023
                ],
1024
            ],
1025
        ];
1026
    }
1027
1028
    /**
1029
     * @return array
1030
     */
1031
    public function getEntityTypeAliasDataProvider()
1032
    {
1033
        return array(
1034
            array(array(
1035
                'fieldName' => 'datetimetz',
1036
                'phpType' => '\\DateTime',
1037
                'dbType' => 'datetimetz',
1038
                'value' => new \DateTime
1039
            )),
1040
            array(array(
1041
                'fieldName' => 'datetime',
1042
                'phpType' => '\\DateTime',
1043
                'dbType' => 'datetime',
1044
                'value' => new \DateTime
1045
            )),
1046
            array(array(
1047
                'fieldName' => 'date',
1048
                'phpType' => '\\DateTime',
1049
                'dbType' => 'date',
1050
                'value' => new \DateTime
1051
            )),
1052
            array(array(
1053
                'fieldName' => 'time',
1054
                'phpType' => '\DateTime',
1055
                'dbType' => 'time',
1056
                'value' => new \DateTime
1057
            )),
1058
            array(array(
1059
                'fieldName' => 'object',
1060
                'phpType' => '\stdClass',
1061
                'dbType' => 'object',
1062
                'value' => new \stdClass()
1063
            )),
1064
            array(array(
1065
                'fieldName' => 'bigint',
1066
                'phpType' => 'int',
1067
                'dbType' => 'bigint',
1068
                'value' => 11
1069
            )),
1070
            array(array(
1071
                'fieldName' => 'smallint',
1072
                'phpType' => 'int',
1073
                'dbType' => 'smallint',
1074
                'value' => 22
1075
            )),
1076
            array(array(
1077
                'fieldName' => 'text',
1078
                'phpType' => 'string',
1079
                'dbType' => 'text',
1080
                'value' => 'text'
1081
            )),
1082
            array(array(
1083
                'fieldName' => 'blob',
1084
                'phpType' => 'string',
1085
                'dbType' => 'blob',
1086
                'value' => 'blob'
1087
            )),
1088
            array(array(
1089
                'fieldName' => 'decimal',
1090
                'phpType' => 'string',
1091
                'dbType' => 'decimal',
1092
                'value' => '12.34'
1093
            ),
1094
        ));
1095
    }
1096
1097
    public function getParseTokensInEntityFileData()
1098
    {
1099
        return array(
1100
            array(
1101
                '<?php namespace Foo\Bar; class Baz {}',
1102
                array('Foo\Bar\Baz'),
1103
            ),
1104
            array(
1105
                '<?php namespace Foo\Bar; use Foo; class Baz {}',
1106
                array('Foo\Bar\Baz'),
1107
            ),
1108
            array(
1109
                '<?php namespace /*Comment*/ Foo\Bar; /** Foo */class /* Comment */ Baz {}',
1110
                array('Foo\Bar\Baz'),
1111
            ),
1112
            array(
1113
                '
1114
<?php namespace
1115
/*Comment*/
1116
Foo\Bar
1117
;
1118
1119
/** Foo */
1120
class
1121
/* Comment */
1122
 Baz {}
1123
     ',
1124
                array('Foo\Bar\Baz'),
1125
            ),
1126
            array(
1127
                '
1128
<?php namespace Foo\Bar; class Baz {
1129
    public static function someMethod(){
1130
        return self::class;
1131
    }
1132
}
1133
',
1134
                array('Foo\Bar\Baz'),
1135
            ),
1136
        );
1137
    }
1138
1139
    /**
1140
     * @param string $type
1141
     * @param \ReflectionProperty $property
1142
     */
1143
    private function assertPhpDocVarType($type, \ReflectionProperty $property)
1144
    {
1145
        $this->assertEquals(1, preg_match('/@var\s+([^\s]+)/',$property->getDocComment(), $matches));
1146
        $this->assertEquals($type, $matches[1]);
1147
    }
1148
1149
    /**
1150
     * @param string $type
1151
     * @param \ReflectionProperty $method
1152
     */
1153
    private function assertPhpDocReturnType($type, \ReflectionMethod $method)
1154
    {
1155
        $this->assertEquals(1, preg_match('/@return\s+([^\s]+)/', $method->getDocComment(), $matches));
1156
        $this->assertEquals($type, $matches[1]);
1157
    }
1158
1159
    /**
1160
     * @param string $type
1161
     * @param \ReflectionProperty $method
1162
     */
1163
    private function assertPhpDocParamType($type, \ReflectionMethod $method)
1164
    {
1165
        $this->assertEquals(1, preg_match('/@param\s+([^\s]+)/', $method->getDocComment(), $matches));
1166
        $this->assertEquals($type, $matches[1]);
1167
    }
1168
}
1169
1170
class EntityGeneratorAuthor {}
1171
class EntityGeneratorComment {}
1172