Completed
Pull Request — master (#1709)
by Andreas
16:45 queued 14:38
created

ProxyFactory::createCloner()   C

Complexity

Conditions 7
Paths 1

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 39.826

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 2
cts 16
cp 0.125
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 1
nop 3
crap 39.826
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Proxy;
4
5
use Doctrine\Common\NotifyPropertyChanged;
6
use Doctrine\Common\Proxy\AbstractProxyFactory;
7
use Doctrine\Common\Proxy\ProxyDefinition;
8
use Doctrine\ODM\MongoDB\DocumentManager;
9
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
10
use Doctrine\Common\Proxy\ProxyGenerator;
11
use Doctrine\Common\Util\ClassUtils;
12
use Doctrine\Common\Proxy\Proxy as BaseProxy;
13
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
14
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
15
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
16
use ReflectionProperty;
17
18
/**
19
 * This factory is used to create proxy objects for documents at runtime.
20
 *
21
 * @since       1.0
22
 */
23
class ProxyFactory extends AbstractProxyFactory
24
{
25
    /**
26
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
27
     */
28
    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...
29
30
    /**
31
     * @var \Doctrine\ODM\MongoDB\UnitOfWork The UnitOfWork this factory is bound to.
32
     */
33
    private $uow;
34
35
    /**
36
     * @var string The namespace that contains all proxy classes.
37
     */
38
    private $proxyNamespace;
39
40
    /**
41
     * @var \Doctrine\Common\EventManager
42
     */
43
    private $lifecycleEventManager;
44
45
    /**
46
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
47
     * connected to the given <tt>DocumentManager</tt>.
48
     *
49
     * @param \Doctrine\ODM\MongoDB\DocumentManager $documentManager The DocumentManager the new factory works for.
50
     * @param string                                $proxyDir        The directory to use for the proxy classes. It
51
     *                                                               must exist.
52
     * @param string                                $proxyNamespace  The namespace to use for the proxy classes.
53
     * @param integer                               $autoGenerate    Whether to automatically generate proxy classes.
54
     */
55 1627
    public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
56
    {
57 1627
        $this->metadataFactory = $documentManager->getMetadataFactory();
58 1627
        $this->uow = $documentManager->getUnitOfWork();
59 1627
        $this->proxyNamespace = $proxyNamespace;
60 1627
        $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...
61 1627
        $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);
62
63 1627
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
64
65 1627
        parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate);
66 1627
    }
67
68
    /**
69
     * {@inheritDoc}
70
     */
71
    public function skipClass(BaseClassMetadata $class)
72
    {
73
        /* @var $class \Doctrine\ODM\Mongodb\Mapping\ClassMetadataInfo */
74
        return $class->isMappedSuperclass || $class->isQueryResultDocument || $class->getReflectionClass()->isAbstract();
75
    }
76
77
    /**
78
     * {@inheritDoc}
79
     */
80 98
    public function createProxyDefinition($className)
81
    {
82
        /* @var $classMetadata \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo */
83 98
        $classMetadata     = $this->metadataFactory->getMetadataFor($className);
84 98
        $documentPersister = $this->uow->getDocumentPersister($className);
85 98
        $reflectionId      = $classMetadata->reflFields[$classMetadata->identifier];
86
87 98
        return new ProxyDefinition(
88 98
            ClassUtils::generateProxyClassName($className, $this->proxyNamespace),
89 98
            $classMetadata->getIdentifierFieldNames(),
90 98
            $classMetadata->getReflectionProperties(),
91 98
            $this->createInitializer($classMetadata, $documentPersister, $reflectionId),
92 98
            $this->createCloner($classMetadata, $documentPersister, $reflectionId)
93
        );
94
    }
95
96
    /**
97
     * Generates a closure capable of initializing a proxy
98
     *
99
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
100
     * @param \Doctrine\ODM\MongoDB\Persisters\DocumentPersister $documentPersister
101
     * @param \ReflectionProperty                                $reflectionId
102
     *
103
     * @return \Closure
104
     *
105
     * @throws \Doctrine\ODM\MongoDB\DocumentNotFoundException
106
     */
107 98
    private function createInitializer(
108
        BaseClassMetadata $classMetadata,
109
        DocumentPersister $documentPersister,
110
        ReflectionProperty $reflectionId
111
    ) {
112 98
        if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
113 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...
114
                $proxy->__setInitializer(null);
115
                $proxy->__setCloner(null);
116
117
                if ($proxy->__isInitialized()) {
118
                    return;
119
                }
120
121
                $properties = $proxy->__getLazyProperties();
122
123
                foreach ($properties as $propertyName => $property) {
124
                    if ( ! isset($proxy->$propertyName)) {
125
                        $proxy->$propertyName = $properties[$propertyName];
126
                    }
127
                }
128
129
                $proxy->__setInitialized(true);
130
                $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...
131
132
                $id = $reflectionId->getValue($proxy);
133
134
                if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
135
                    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...
136
                        throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
137
                    }
138
                }
139
140
                if ($proxy instanceof NotifyPropertyChanged) {
141
                    $proxy->addPropertyChangedListener($this->uow);
142
                }
143
            };
144
        }
145
146 98 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...
147 44
            $proxy->__setInitializer(null);
148 44
            $proxy->__setCloner(null);
149
150 44
            if ($proxy->__isInitialized()) {
151 3
                return;
152
            }
153
154 44
            $properties = $proxy->__getLazyProperties();
155
156 44
            foreach ($properties as $propertyName => $property) {
157 25
                if ( ! isset($proxy->$propertyName)) {
158 25
                    $proxy->$propertyName = $properties[$propertyName];
159
                }
160
            }
161
162 44
            $proxy->__setInitialized(true);
163
164 44
            $id = $reflectionId->getValue($proxy);
165
166 44
            if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
167 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...
168 8
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
169
                }
170
            }
171
172 40
            if ($proxy instanceof NotifyPropertyChanged) {
173 1
                $proxy->addPropertyChangedListener($this->uow);
174
            }
175 98
        };
176
    }
177
178
    /**
179
     * Generates a closure capable of finalizing a cloned proxy
180
     *
181
     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata
182
     * @param \Doctrine\ODM\MongoDB\Persisters\DocumentPersister $documentPersister
183
     * @param \ReflectionProperty                                $reflectionId
184
     *
185
     * @return \Closure
186
     *
187
     * @throws \Doctrine\ODM\MongoDB\DocumentNotFoundException
188
     */
189
    private function createCloner(
190
        BaseClassMetadata $classMetadata,
191
        DocumentPersister $documentPersister,
192
        ReflectionProperty $reflectionId
193
    ) {
194 98
        return function (BaseProxy $proxy) use ($documentPersister, $classMetadata, $reflectionId) {
195
            if ($proxy->__isInitialized()) {
196
                return;
197
            }
198
199
            $proxy->__setInitialized(true);
200
            $proxy->__setInitializer(null);
201
202
            $id       = $reflectionId->getValue($proxy);
203
            $original = $documentPersister->load(array('_id' => $id));
204
205
            if (null === $original) {
206
                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...
207
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
208
                }
209
            }
210
211
            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
212
                $propertyName = $reflectionProperty->getName();
213
214
                if ($classMetadata->hasField($propertyName) || $classMetadata->hasAssociation($propertyName)) {
215
                    $reflectionProperty->setAccessible(true);
216
                    $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
217
                }
218
            }
219 98
        };
220
    }
221
}
222