Failed Conditions
Push — master ( 57a950...9005c5 )
by Marco
23:43 queued 08:30
created

ProxyFactory   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 96.92%

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 13
dl 0
loc 171
ccs 63
cts 65
cp 0.9692
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A skipClass() 0 7 3
A createProxyDefinition() 0 13 1
B createInitializer() 0 43 6
B createCloner() 0 31 6
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Proxy;
21
22
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
23
use Doctrine\Common\Proxy\AbstractProxyFactory;
24
use Doctrine\Common\Proxy\Proxy as BaseProxy;
25
use Doctrine\Common\Proxy\ProxyDefinition;
26
use Doctrine\Common\Proxy\ProxyGenerator;
27
use Doctrine\Common\Util\ClassUtils;
28
use Doctrine\ORM\EntityManagerInterface;
29
use Doctrine\ORM\Persisters\Entity\EntityPersister;
30
use Doctrine\ORM\EntityNotFoundException;
31
use Doctrine\ORM\Utility\IdentifierFlattener;
32
33
/**
34
 * This factory is used to create proxy objects for entities at runtime.
35
 *
36
 * @author Roman Borschel <[email protected]>
37
 * @author Giorgio Sironi <[email protected]>
38
 * @author Marco Pivetta  <[email protected]>
39
 * @since 2.0
40
 */
41
class ProxyFactory extends AbstractProxyFactory
42
{
43
    /**
44
     * @var EntityManagerInterface The EntityManager this factory is bound to.
45
     */
46
    private $em;
47
48
    /**
49
     * @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters
50
     */
51
    private $uow;
52
53
    /**
54
     * @var string
55
     */
56
    private $proxyNs;
57
58
    /**
59
     * The IdentifierFlattener used for manipulating identifiers
60
     *
61
     * @var \Doctrine\ORM\Utility\IdentifierFlattener
62
     */
63
    private $identifierFlattener;
64
65
    /**
66
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
67
     * connected to the given <tt>EntityManager</tt>.
68
     *
69
     * @param EntityManagerInterface $em           The EntityManager the new factory works for.
70
     * @param string                 $proxyDir     The directory to use for the proxy classes. It must exist.
71
     * @param string                 $proxyNs      The namespace to use for the proxy classes.
72
     * @param boolean|int            $autoGenerate The strategy for automatically generating proxy classes. Possible
73
     *                                             values are constants of Doctrine\Common\Proxy\AbstractProxyFactory.
74
     */
75 2398
    public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
76
    {
77 2398
        $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
78
79 2398
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
80 2398
        parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
81
82 2398
        $this->em                  = $em;
83 2398
        $this->uow                 = $em->getUnitOfWork();
84 2398
        $this->proxyNs             = $proxyNs;
85 2398
        $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());
86 2398
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 3
    protected function skipClass(ClassMetadata $metadata)
92
    {
93
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
94 3
        return $metadata->isMappedSuperclass
95 2
            || $metadata->isEmbeddedClass
96 3
            || $metadata->getReflectionClass()->isAbstract();
97
    }
98
99
    /**
100
     * {@inheritDoc}
101
     */
102 232
    protected function createProxyDefinition($className)
103
    {
104 232
        $classMetadata   = $this->em->getClassMetadata($className);
105 232
        $entityPersister = $this->uow->getEntityPersister($className);
106
107 232
        return new ProxyDefinition(
108 232
            ClassUtils::generateProxyClassName($className, $this->proxyNs),
109 232
            $classMetadata->getIdentifierFieldNames(),
110 232
            $classMetadata->getReflectionProperties(),
111 232
            $this->createInitializer($classMetadata, $entityPersister),
112 232
            $this->createCloner($classMetadata, $entityPersister)
113
        );
114
    }
115
116
    /**
117
     * Creates a closure capable of initializing a proxy
118
     *
119
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
120
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister    $entityPersister
121
     *
122
     * @return \Closure
123
     *
124
     * @throws \Doctrine\ORM\EntityNotFoundException
125
     */
126 232
    private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister)
127
    {
128 232
        $wakeupProxy = $classMetadata->getReflectionClass()->hasMethod('__wakeup');
129
130 232
        return function (BaseProxy $proxy) use ($entityPersister, $classMetadata, $wakeupProxy) {
131 95
            $initializer = $proxy->__getInitializer();
132 95
            $cloner      = $proxy->__getCloner();
133
134 95
            $proxy->__setInitializer(null);
135 95
            $proxy->__setCloner(null);
136
137 95
            if ($proxy->__isInitialized()) {
138 20
                return;
139
            }
140
141 75
            $properties = $proxy->__getLazyProperties();
142
143 75
            foreach ($properties as $propertyName => $property) {
144 38
                if ( ! isset($proxy->$propertyName)) {
145 38
                    $proxy->$propertyName = $properties[$propertyName];
146
                }
147
            }
148
149 75
            $proxy->__setInitialized(true);
150
151 75
            if ($wakeupProxy) {
152 9
                $proxy->__wakeup();
0 ignored issues
show
Bug introduced by
The method __wakeup() does not seem to exist on object<Doctrine\Common\Proxy\Proxy>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
153
            }
154
155 75
            $identifier = $classMetadata->getIdentifierValues($proxy);
156
157 75
            if (null === $entityPersister->loadById($identifier, $proxy)) {
158 1
                $proxy->__setInitializer($initializer);
159 1
                $proxy->__setCloner($cloner);
160 1
                $proxy->__setInitialized(false);
161
162 1
                throw EntityNotFoundException::fromClassNameAndIdentifier(
163 1
                    $classMetadata->getName(),
164 1
                    $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier)
0 ignored issues
show
Compatibility introduced by
$classMetadata 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...
165
                );
166
            }
167 232
        };
168
    }
169
170
    /**
171
     * Creates a closure capable of finalizing state a cloned proxy
172
     *
173
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
174
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister    $entityPersister
175
     *
176
     * @return \Closure
177
     *
178
     * @throws \Doctrine\ORM\EntityNotFoundException
179
     */
180
    private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister)
181
    {
182 232
        return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) {
183 4
            if ($proxy->__isInitialized()) {
184
                return;
185
            }
186
187 4
            $proxy->__setInitialized(true);
188 4
            $proxy->__setInitializer(null);
189
190 4
            $class      = $entityPersister->getClassMetadata();
191 4
            $identifier = $classMetadata->getIdentifierValues($proxy);
192 4
            $original   = $entityPersister->loadById($identifier);
193
194 4
            if (null === $original) {
195 1
                throw EntityNotFoundException::fromClassNameAndIdentifier(
196 1
                    $classMetadata->getName(),
197 1
                    $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier)
0 ignored issues
show
Compatibility introduced by
$classMetadata 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...
198
                );
199
            }
200
201 3
            foreach ($class->getReflectionProperties() as $property) {
202 3
                if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
203
                    continue;
204
                }
205
206 3
                $property->setAccessible(true);
207 3
                $property->setValue($proxy, $property->getValue($original));
208
            }
209 232
        };
210
    }
211
}
212