Completed
Push — master ( dba66b...b8eb92 )
by Fabien
03:50
created

ContainerRepositoryFactory::__construct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of the Doctrine Bundle
5
 *
6
 * The code was originally distributed inside the Symfony framework.
7
 *
8
 * (c) Fabien Potencier <[email protected]>
9
 * (c) Doctrine Project, Benjamin Eberlei <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Doctrine\Bundle\DoctrineBundle\Repository;
16
17
use Doctrine\ORM\EntityManagerInterface;
18
use Doctrine\ORM\EntityRepository;
19
use Doctrine\ORM\Mapping\ClassMetadata;
20
use Doctrine\ORM\Repository\RepositoryFactory;
21
use Psr\Container\ContainerInterface;
22
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
23
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
24
25
/**
26
 * Fetches repositories from the container or falls back to normal creation.
27
 *
28
 * @author Ryan Weaver <[email protected]>
29
 */
30
final class ContainerRepositoryFactory implements RepositoryFactory
31
{
32
    private $managedRepositories = [];
33
34
    private $container;
35
36
    /**
37
     * @param ContainerInterface $container A service locator containing the repositories
0 ignored issues
show
Documentation introduced by
Should the type for parameter $container not be null|ContainerInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
38
     */
39
    public function __construct(ContainerInterface $container = null)
40
    {
41
        // When DoctrineBundle requires Symfony 3.3+, this can be removed
42
        // and the $container argument can become required.
43
        if (null === $container && class_exists(ServiceLocatorTagPass::class)) {
44
            throw new \InvalidArgumentException(sprintf('The first argument of %s::__construct() is required on Symfony 3.3 or higher.', self::class));
45
        }
46
47
        $this->container = $container;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function getRepository(EntityManagerInterface $entityManager, $entityName)
54
    {
55
        $metadata = $entityManager->getClassMetadata($entityName);
56
        $repositoryServiceId = $metadata->customRepositoryClassName;
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName 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...
57
58
        $customRepositoryName = $metadata->customRepositoryClassName;
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName 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...
59
        if (null !== $customRepositoryName) {
60
            // fetch from the container
61
            if ($this->container && $this->container->has($customRepositoryName)) {
62
                $repository = $this->container->get($customRepositoryName);
63
64
                if (!$repository instanceof EntityRepository) {
65
                    throw new \RuntimeException(sprintf('The service "%s" must extend EntityRepository (or a base class, like ServiceEntityRepository).', $repositoryServiceId));
66
                }
67
68
                return $repository;
69
            }
70
71
            // if not in the container but the class/id implements the interface, throw an error
72
            if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
73
                // can be removed when DoctrineBundle requires Symfony 3.3
74
                if (null === $this->container) {
75
                    throw new \RuntimeException(sprintf('Support for loading entities from the service container only works for Symfony 3.3 or higher. Upgrade your version of Symfony or make sure your "%s" class does not implement "%s"', $customRepositoryName, ServiceEntityRepositoryInterface::class));
76
                }
77
78
                throw new \RuntimeException(sprintf('The "%s" entity repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".', $customRepositoryName, ServiceEntityRepositoryInterface::class, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
79
            }
80
81
            if (!class_exists($customRepositoryName)) {
82
                throw new \RuntimeException(sprintf('The "%s" entity has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".', $metadata->name, $customRepositoryName, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
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...
83
            }
84
85
            // allow the repository to be created below
86
        }
87
88
        return $this->getOrCreateRepository($entityManager, $metadata);
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\ClassMetadata>. 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...
89
    }
90
91
    private function getOrCreateRepository(EntityManagerInterface $entityManager, ClassMetadata $metadata)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
92
    {
93
        $repositoryHash = $metadata->getName().spl_object_hash($entityManager);
94
        if (isset($this->managedRepositories[$repositoryHash])) {
95
            return $this->managedRepositories[$repositoryHash];
96
        }
97
98
        $repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
99
100
        return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
101
    }
102
}
103