Failed Conditions
Push — master ( 8149a1...97914c )
by Marco
19s
created

ProxyFactory::createInitializer()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 43
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 43
ccs 25
cts 25
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 24
nc 1
nop 2
crap 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 2391
    public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
76
    {
77 2391
        $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
78
79 2391
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
80 2391
        parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
81
82 2391
        $this->em                  = $em;
83 2391
        $this->uow                 = $em->getUnitOfWork();
84 2391
        $this->proxyNs             = $proxyNs;
85 2391
        $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());
86 2391
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 1
    protected function skipClass(ClassMetadata $metadata)
92
    {
93
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
94 1
        return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract();
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 232
    protected function createProxyDefinition($className)
101
    {
102 232
        $classMetadata   = $this->em->getClassMetadata($className);
103 232
        $entityPersister = $this->uow->getEntityPersister($className);
104
105 232
        return new ProxyDefinition(
106 232
            ClassUtils::generateProxyClassName($className, $this->proxyNs),
107 232
            $classMetadata->getIdentifierFieldNames(),
108 232
            $classMetadata->getReflectionProperties(),
109 232
            $this->createInitializer($classMetadata, $entityPersister),
110 232
            $this->createCloner($classMetadata, $entityPersister)
111
        );
112
    }
113
114
    /**
115
     * Creates a closure capable of initializing a proxy
116
     *
117
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
118
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister    $entityPersister
119
     *
120
     * @return \Closure
121
     *
122
     * @throws \Doctrine\ORM\EntityNotFoundException
123
     */
124 232
    private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister)
125
    {
126 232
        $wakeupProxy = $classMetadata->getReflectionClass()->hasMethod('__wakeup');
127
128 232
        return function (BaseProxy $proxy) use ($entityPersister, $classMetadata, $wakeupProxy) {
129 95
            $initializer = $proxy->__getInitializer();
130 95
            $cloner      = $proxy->__getCloner();
131
132 95
            $proxy->__setInitializer(null);
133 95
            $proxy->__setCloner(null);
134
135 95
            if ($proxy->__isInitialized()) {
136 20
                return;
137
            }
138
139 75
            $properties = $proxy->__getLazyProperties();
140
141 75
            foreach ($properties as $propertyName => $property) {
142 38
                if ( ! isset($proxy->$propertyName)) {
143 38
                    $proxy->$propertyName = $properties[$propertyName];
144
                }
145
            }
146
147 75
            $proxy->__setInitialized(true);
148
149 75
            if ($wakeupProxy) {
150 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...
151
            }
152
153 75
            $identifier = $classMetadata->getIdentifierValues($proxy);
154
155 75
            if (null === $entityPersister->loadById($identifier, $proxy)) {
156 1
                $proxy->__setInitializer($initializer);
157 1
                $proxy->__setCloner($cloner);
158 1
                $proxy->__setInitialized(false);
159
160 1
                throw EntityNotFoundException::fromClassNameAndIdentifier(
161 1
                    $classMetadata->getName(),
162 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...
163
                );
164
            }
165 232
        };
166
    }
167
168
    /**
169
     * Creates a closure capable of finalizing state a cloned proxy
170
     *
171
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
172
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister    $entityPersister
173
     *
174
     * @return \Closure
175
     *
176
     * @throws \Doctrine\ORM\EntityNotFoundException
177
     */
178
    private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister)
179
    {
180 232
        return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) {
181 4
            if ($proxy->__isInitialized()) {
182
                return;
183
            }
184
185 4
            $proxy->__setInitialized(true);
186 4
            $proxy->__setInitializer(null);
187
188 4
            $class      = $entityPersister->getClassMetadata();
189 4
            $identifier = $classMetadata->getIdentifierValues($proxy);
190 4
            $original   = $entityPersister->loadById($identifier);
191
192 4
            if (null === $original) {
193 1
                throw EntityNotFoundException::fromClassNameAndIdentifier(
194 1
                    $classMetadata->getName(),
195 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...
196
                );
197
            }
198
199 3
            foreach ($class->getReflectionProperties() as $property) {
200 3
                if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
201
                    continue;
202
                }
203
204 3
                $property->setAccessible(true);
205 3
                $property->setValue($proxy, $property->getValue($original));
206
            }
207 232
        };
208
    }
209
}
210