Completed
Pull Request — master (#5569)
by Jérémy
67:47 queued 04:57
created

EntityGeneratorTest   D

Complexity

Total Complexity 53

Size/Duplication

Total Lines 1178
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12
Metric Value
wmc 53
lcom 1
cbo 12
dl 0
loc 1178
rs 4.8543

39 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 15 1
A tearDown() 0 11 3
B generateBookEntityFixture() 0 39 2
A generateEntityTypeFixture() 0 17 1
A generateIsbnEmbeddableFixture() 0 19 2
A generateTestEmbeddableFixture() 0 14 1
A mapEmbedded() 0 10 1
A mapNestedEmbedded() 0 17 4
A loadEntityClass() 0 9 1
A newInstance() 0 6 1
A testGeneratedEntityClass() 0 61 1
B testEntityUpdatingWorks() 0 40 1
B testDoesNotRegenerateExistingMethodsWithDifferentCase() 0 26 1
A testMethodDocBlockShouldStartWithBackSlash() 0 22 1
A testEntityExtendsStdClass() 0 12 1
A testLifecycleCallbacks() 0 10 1
B testLoadMetadata() 0 37 1
B testLoadPrefixedMetadata() 0 36 1
A testMappedSuperclassAnnotationGeneration() 0 18 1
A testParseTokensInEntityFile() 0 12 1
B testGenerateEntityWithSequenceGenerator() 0 28 1
B testGenerateEntityWithMultipleInverseJoinColumns() 0 40 1
A testGetInheritanceTypeString() 0 23 3
A testGetChangeTrackingPolicyString() 0 23 3
A testGetIdGeneratorTypeString() 0 23 3
B testEntityTypeAlias() 0 24 1
A testTraitPropertiesAndMethodsAreNotDuplicated() 0 22 1
A testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses() 0 22 1
A testMethodsAndPropertiesAreNotDuplicatedInChildClasses() 0 69 1
B testGeneratedMutableEmbeddablesClass() 0 35 1
A testGeneratedImmutableEmbeddablesClass() 0 70 1
A testRegenerateEntityClass() 0 17 1
A testGenerateMethodsTypeHinting() 0 58 1
B testEntityMethodTypeHintingAlias() 0 28 2
A getEntityTypeAliasDataProvider() 0 65 1
B getParseTokensInEntityFileData() 0 41 1
A assertPhpDocVarType() 0 5 1
A assertPhpDocReturnType() 0 5 1
A assertPhpDocParamType() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like EntityGeneratorTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityGeneratorTest, and based on these observations, apply Extract Interface, too.

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