Completed
Pull Request — master (#1336)
by Andreas
13:16
created

ProxyFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 12
ccs 9
cts 9
cp 1
crap 1
rs 9.4285
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\ODM\MongoDB\Proxy;
21
22
use Doctrine\Common\NotifyPropertyChanged;
23
use Doctrine\Common\Proxy\AbstractProxyFactory;
24
use Doctrine\Common\Proxy\ProxyDefinition;
25
use Doctrine\ODM\MongoDB\DocumentManager;
26
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
27
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
28
use Doctrine\Common\Proxy\ProxyGenerator;
29
use Doctrine\Common\Util\ClassUtils;
30
use Doctrine\Common\Proxy\Proxy as BaseProxy;
31
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
32
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
33
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
34
use ReflectionProperty;
35
36
/**
37
 * This factory is used to create proxy objects for documents at runtime.
38
 *
39
 * @since       1.0
40
 */
41
class ProxyFactory extends AbstractProxyFactory
42
{
43
    /**
44
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
45
     */
46
    private $metadataFactory;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
47
48
    /**
49
     * @var \Doctrine\ODM\MongoDB\UnitOfWork The UnitOfWork this factory is bound to.
50
     */
51
    private $uow;
52
53
    /**
54
     * @var string The namespace that contains all proxy classes.
55
     */
56
    private $proxyNamespace;
57
58
    /**
59
     * @var \Doctrine\Common\EventManager
60
     */
61
    private $lifecycleEventManager;
62
63
    /**
64
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
65
     * connected to the given <tt>DocumentManager</tt>.
66
     *
67
     * @param \Doctrine\ODM\MongoDB\DocumentManager $documentManager The DocumentManager the new factory works for.
68
     * @param string                                $proxyDir        The directory to use for the proxy classes. It
69
     *                                                               must exist.
70
     * @param string                                $proxyNamespace  The namespace to use for the proxy classes.
71
     * @param integer                               $autoGenerate    Whether to automatically generate proxy classes.
72
     */
73 940
    public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
74
    {
75 940
        $this->metadataFactory = $documentManager->getMetadataFactory();
76 940
        $this->uow = $documentManager->getUnitOfWork();
77 940
        $this->proxyNamespace = $proxyNamespace;
78 940
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\ODM\MongoD...ger->getEventManager()) of type object<Doctrine\ODM\Mong...\LifecycleEventManager> is incompatible with the declared type object<Doctrine\Common\EventManager> of property $lifecycleEventManager.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
79 940
        $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);
80
81 940
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
82
83 940
        parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate);
84 940
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89
    public function skipClass(BaseClassMetadata $class)
90
    {
91
        /* @var $class \Doctrine\ODM\Mongodb\Mapping\ClassMetadataInfo */
92
        return $class->isMappedSuperclass || $class->getReflectionClass()->isAbstract();
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98 88
    public function createProxyDefinition($className)
99
    {
100
        /* @var $classMetadata \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo */
101 88
        $classMetadata     = $this->metadataFactory->getMetadataFor($className);
102 88
        $documentPersister = $this->uow->getDocumentPersister($className);
103 88
        $reflectionId      = $classMetadata->reflFields[$classMetadata->identifier];
104
105 88
        return new ProxyDefinition(
106 88
            ClassUtils::generateProxyClassName($className, $this->proxyNamespace),
107 88
            $classMetadata->getIdentifierFieldNames(),
108 88
            $classMetadata->getReflectionProperties(),
109 88
            $this->createInitializer($classMetadata, $documentPersister, $reflectionId),
110 88
            $this->createCloner($classMetadata, $documentPersister, $reflectionId)
111 88
        );
112
    }
113
114
    /**
115
     * Generates a closure capable of initializing a proxy
116
     *
117
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
118
     * @param \Doctrine\ODM\MongoDB\Persisters\DocumentPersister $documentPersister
119
     * @param \ReflectionProperty                                $reflectionId
120
     *
121
     * @return \Closure
122
     *
123
     * @throws \Doctrine\ODM\MongoDB\DocumentNotFoundException
124
     */
125 88
    private function createInitializer(
126
        BaseClassMetadata $classMetadata,
127
        DocumentPersister $documentPersister,
128
        ReflectionProperty $reflectionId
129
    ) {
130 88
        if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
131 View Code Duplication
            return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
                $proxy->__setInitializer(null);
133
                $proxy->__setCloner(null);
134
135
                if ($proxy->__isInitialized()) {
136
                    return;
137
                }
138
139
                $properties = $proxy->__getLazyProperties();
140
141
                foreach ($properties as $propertyName => $property) {
142
                    if ( ! isset($proxy->$propertyName)) {
143
                        $proxy->$propertyName = $properties[$propertyName];
144
                    }
145
                }
146
147
                $proxy->__setInitialized(true);
148
                $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...
149
150
                $id = $reflectionId->getValue($proxy);
151
152
                if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
153
                    if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
Bug introduced by
The method documentNotFound() does not seem to exist on object<Doctrine\Common\EventManager>.

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...
154
                        throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
155
                    }
156
                }
157
158
                if ($proxy instanceof NotifyPropertyChanged) {
159
                    $proxy->addPropertyChangedListener($this->uow);
160
                }
161
            };
162
        }
163
164 View Code Duplication
        return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165 49
            $proxy->__setInitializer(null);
166 49
            $proxy->__setCloner(null);
167
168 49
            if ($proxy->__isInitialized()) {
169 12
                return;
170
            }
171
172 41
            $properties = $proxy->__getLazyProperties();
173
174 41
            foreach ($properties as $propertyName => $property) {
175 21
                if ( ! isset($proxy->$propertyName)) {
176 21
                    $proxy->$propertyName = $properties[$propertyName];
177 21
                }
178 41
            }
179
180 41
            $proxy->__setInitialized(true);
181
182 41
            $id = $reflectionId->getValue($proxy);
183
184 41
            if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
185 9
                if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
Bug introduced by
The method documentNotFound() does not seem to exist on object<Doctrine\Common\EventManager>.

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...
186 8
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
187
                }
188 1
            }
189
190 37
            if ($proxy instanceof NotifyPropertyChanged) {
191 1
                $proxy->addPropertyChangedListener($this->uow);
192 1
            }
193 88
        };
194
    }
195
196
    /**
197
     * Generates a closure capable of finalizing a cloned proxy
198
     *
199
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
200
     * @param \Doctrine\ODM\MongoDB\Persisters\DocumentPersister $documentPersister
201
     * @param \ReflectionProperty                                $reflectionId
202
     *
203
     * @return \Closure
204
     *
205
     * @throws \Doctrine\ODM\MongoDB\DocumentNotFoundException
206
     */
207
    private function createCloner(
208
        BaseClassMetadata $classMetadata,
209
        DocumentPersister $documentPersister,
210
        ReflectionProperty $reflectionId
211
    ) {
212 88
        return function (BaseProxy $proxy) use ($documentPersister, $classMetadata, $reflectionId) {
213
            if ($proxy->__isInitialized()) {
214
                return;
215
            }
216
217
            $proxy->__setInitialized(true);
218
            $proxy->__setInitializer(null);
219
220
            $id       = $reflectionId->getValue($proxy);
221
            $original = $documentPersister->load(array('_id' => $id));
222
223
            if (null === $original) {
224
                if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
Bug introduced by
The method documentNotFound() does not seem to exist on object<Doctrine\Common\EventManager>.

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...
225
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
226
                }
227
            }
228
229
            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
230
                $propertyName = $reflectionProperty->getName();
231
232
                if ($classMetadata->hasField($propertyName) || $classMetadata->hasAssociation($propertyName)) {
233
                    $reflectionProperty->setAccessible(true);
234
                    $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
235
                }
236
            }
237 88
        };
238
    }
239
}
240