Skip to content

Commit b769877

Browse files
authored
Merge pull request #2115 from jtattevin/fix-2111-detect-mapping-type-with-package
Fix detectMappingType if the entity contain a package attribute
2 parents 6a620f9 + 126ec9e commit b769877

File tree

6 files changed

+130
-2
lines changed

6 files changed

+130
-2
lines changed

src/DependencyInjection/DoctrineExtension.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,8 @@ private function detectMappingType(string $directory, ContainerBuilder $containe
427427
}
428428

429429
if (
430-
preg_match('/^(?: \*|\/\*\*) @.*' . $quotedMappingObjectName . '\b/m', $content)
431-
|| preg_match('/^(?: \*|\/\*\*) @.*Embeddable\b/m', $content)
430+
self::textContainsAnnotation($quotedMappingObjectName, $content)
431+
|| self::textContainsAnnotation('Embeddable', $content)
432432
) {
433433
$type = 'annotation';
434434
break;
@@ -438,6 +438,21 @@ private function detectMappingType(string $directory, ContainerBuilder $containe
438438
return $type;
439439
}
440440

441+
/**
442+
* Check if the file content contains a class-like annotation
443+
*
444+
* @internal
445+
*/
446+
public static function textContainsAnnotation(string $quotedMappingObjectName, string $content): bool
447+
{
448+
return preg_match('/^(?:[ ]\*|\/\*\*)[ ]@ # Match phpdoc start or line with an at
449+
\\\\? # Can start with antislash
450+
([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*\\\\)* # Match namespace components ending with antislash
451+
' . $quotedMappingObjectName . ' # The target class
452+
\b # Match word boundary
453+
/mx', $content) === 1;
454+
}
455+
441456
/**
442457
* Returns a modified version of $managerConfigs.
443458
*

tests/DependencyInjection/DoctrineExtensionTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,71 @@ public function testControllerResolver(bool $simpleEntityManagerConfig): void
15071507
$this->assertEquals(new MapEntity(null, null, null, [], null, null, null, true, true), $container->get('controller_resolver_defaults'));
15081508
}
15091509

1510+
#[TestWith(['AnnotationsBundle', 'attribute', 'Vendor'], 'Bundle without anything')]
1511+
#[TestWith(['AttributesBundle', 'attribute'], 'Bundle with attributes')]
1512+
#[TestWith(['RepositoryServiceBundle', 'attribute'], 'Bundle with both')]
1513+
#[TestWith(['AnnotationsBundle', 'annotation'], 'Bundle with annotations')]
1514+
#[TestWith(['AttributesWithPackageBundle', 'attribute'], 'Bundle with attributes and @package')]
1515+
public function testDetectMappingType(string $bundle, string $expectedType, string $vendor = '')
1516+
{
1517+
if (! interface_exists(EntityManagerInterface::class)) {
1518+
self::markTestSkipped('This test requires ORM');
1519+
}
1520+
1521+
$container = $this->getContainer([$bundle], $vendor);
1522+
$extension = new DoctrineExtension();
1523+
1524+
$config = BundleConfigurationBuilder::createBuilder()
1525+
->addBaseConnection()
1526+
->addEntityManager([
1527+
'default_entity_manager' => 'default',
1528+
'entity_managers' => [
1529+
'default' => [
1530+
'mappings' => [
1531+
$bundle => [],
1532+
],
1533+
],
1534+
],
1535+
])
1536+
->build();
1537+
1538+
if (! class_exists(AnnotationDriver::class) && $expectedType === 'annotation') {
1539+
$this->expectException(LogicException::class);
1540+
$this->expectExceptionMessage('The annotation driver is only available in doctrine/orm v2.');
1541+
}
1542+
1543+
$extension->load([$config], $container);
1544+
1545+
$calls = $container->getDefinition('doctrine.orm.default_metadata_driver')->getMethodCalls();
1546+
$this->assertEquals(
1547+
sprintf('doctrine.orm.default_%s_metadata_driver', $expectedType),
1548+
(string) $calls[0][1][0],
1549+
);
1550+
}
1551+
1552+
#[TestWith([' * @Mapping\\Entity', true], 'Using the namespace without alias')]
1553+
#[TestWith([' * @ORM\\Entity', true], 'Using the namespace with alias')]
1554+
#[TestWith([' * @\\Doctrine\\ORM\\Mapping\\Entity', true], 'Complete namespace with starting slash')]
1555+
#[TestWith([' * @Doctrine\\ORM\\Mapping\\Entity', true], 'Complete namespace without starting slash')]
1556+
#[TestWith([' * @Entity', true], 'Use of the class')]
1557+
#[TestWith([' * @Entity()', true], 'With parentheses')]
1558+
#[TestWith(['/** @Entity */', true], 'Comment start')]
1559+
#[TestWith(["/**\n * @Entity\n */", true], 'Multiline phpdoc')]
1560+
#[TestWith([' * @orm\\Entity', true], 'namespace can start with lowercase')]
1561+
#[TestWith([' * @_ORM\\Entity', true], 'namespace can start with underscore')]
1562+
#[TestWith([" * @\x80ORM\\Entity", true], 'namespace can start with char from x80-Xff')]
1563+
#[TestWith([" * @orm0_\x80\\Entity", true], 'namespace can contain number, underscore and char from x80-Xff')]
1564+
#[TestWith([' * @ORMEntity', false], 'Use of the class with prefix')]
1565+
#[TestWith([' * @EntityORM', false], 'Use of the class with suffix')]
1566+
#[TestWith([' * @package testEntity', false], 'Annotation with Entity as value')]
1567+
#[TestWith([' * @entity', false], 'Lowercase use of the class')]
1568+
#[TestWith([' * @1ORMEntity', false], 'namespace can\'t start with number')]
1569+
#[TestWith([' * @extend<Entity>', false], 'The Entity is used inside < and >')]
1570+
public function testTextContainsAnnotation(string $input, bool $expected): void
1571+
{
1572+
self::assertEquals($expected, DoctrineExtension::textContainsAnnotation('Entity', $input));
1573+
}
1574+
15101575
/** @param list<string> $bundles */
15111576
private static function getContainer(array $bundles = ['XmlBundle'], string $vendor = ''): ContainerBuilder
15121577
{
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fixtures\Bundles\AnnotationsBundle;
6+
7+
use Symfony\Component\HttpKernel\Bundle\Bundle;
8+
9+
class AnnotationsBundle extends Bundle
10+
{
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fixtures\Bundles\AnnotationsBundle\Entity;
6+
7+
/** @ORM\Entity() */
8+
class TestAnnotationEntity
9+
{
10+
/**
11+
* @ORM\Id
12+
* @ORM\GeneratedValue(strategy="AUTO")
13+
* @ORM\Column(type="integer")
14+
*/
15+
public int|null $id = null;
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fixtures\Bundles\AttributesWithPackageBundle;
6+
7+
use Symfony\Component\HttpKernel\Bundle\Bundle;
8+
9+
class AttributesWithPackageBundle extends Bundle
10+
{
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fixtures\Bundles\AttributesWithPackageBundle\Entity;
6+
7+
/** @testUnrelatedAnnotation Fixtures\Bundles\AttributesWithPackageBundle\Entity */
8+
interface TestInterface
9+
{
10+
}

0 commit comments

Comments
 (0)