Completed
Push — master ( f3ed76...7db4b5 )
by Andreas
14s queued 10s
created

lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Proxy;
6
7
use Closure;
8
use Doctrine\Common\EventManager;
9
use Doctrine\Common\NotifyPropertyChanged;
10
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
11
use Doctrine\Common\Proxy\AbstractProxyFactory;
12
use Doctrine\Common\Proxy\Proxy as BaseProxy;
13
use Doctrine\Common\Proxy\ProxyDefinition;
14
use Doctrine\Common\Proxy\ProxyGenerator;
15
use Doctrine\Common\Util\ClassUtils;
16
use Doctrine\ODM\MongoDB\DocumentManager;
17
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
18
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
19
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
20
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
21
use Doctrine\ODM\MongoDB\UnitOfWork;
22
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
23
use ReflectionProperty;
24
use function get_class;
25
26
/**
27
 * This factory is used to create proxy objects for documents at runtime.
28
 */
29
class ProxyFactory extends AbstractProxyFactory
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Proxy\AbstractProxyFactory has been deprecated with message: The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
30
{
31
    /** @var ClassMetadataFactory */
32
    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...
33
34
    /** @var UnitOfWork The UnitOfWork this factory is bound to. */
35
    private $uow;
36
37
    /** @var string The namespace that contains all proxy classes. */
38
    private $proxyNamespace;
39
40
    /** @var EventManager */
41
    private $lifecycleEventManager;
42
43
    /**
44
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
45
     * connected to the given <tt>DocumentManager</tt>.
46
     *
47
     * @param DocumentManager $documentManager The DocumentManager the new factory works for.
48
     * @param string          $proxyDir        The directory to use for the proxy classes. It
49
     *                                         must exist.
50
     * @param string          $proxyNamespace  The namespace to use for the proxy classes.
51
     * @param int             $autoGenerate    Whether to automatically generate proxy classes.
52
     */
53 1633
    public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
54
    {
55 1633
        $this->metadataFactory       = $documentManager->getMetadataFactory();
56 1633
        $this->uow                   = $documentManager->getUnitOfWork();
57 1633
        $this->proxyNamespace        = $proxyNamespace;
58 1633
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
59 1633
        $proxyGenerator              = new ProxyGenerator($proxyDir, $proxyNamespace);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Proxy\ProxyGenerator has been deprecated with message: The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
60
61 1633
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
62
63 1633
        parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate);
64 1633
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function skipClass(BaseClassMetadata $class)
70
    {
71
        /** @var ClassMetadata $class */
72
        return $class->isMappedSuperclass || $class->isQueryResultDocument || $class->getReflectionClass()->isAbstract();
73
    }
74
75
    /**
76
     * {@inheritDoc}
77
     */
78 105
    public function createProxyDefinition($className)
79
    {
80
        /** @var ClassMetadata $classMetadata */
81 105
        $classMetadata     = $this->metadataFactory->getMetadataFor($className);
82 105
        $documentPersister = $this->uow->getDocumentPersister($className);
83 105
        $reflectionId      = $classMetadata->reflFields[$classMetadata->identifier];
84
85 105
        return new ProxyDefinition(
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Proxy\ProxyDefinition has been deprecated with message: The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
86 105
            ClassUtils::generateProxyClassName($className, $this->proxyNamespace),
87 105
            $classMetadata->getIdentifierFieldNames(),
88 105
            $classMetadata->getReflectionProperties(),
89 105
            $this->createInitializer($classMetadata, $documentPersister, $reflectionId),
90 105
            $this->createCloner($classMetadata, $documentPersister, $reflectionId)
91
        );
92
    }
93
94
    /**
95
     * Generates a closure capable of initializing a proxy
96
     *
97
     * @return Closure
98
     *
99
     * @throws DocumentNotFoundException
100
     */
101 105
    private function createInitializer(
102
        BaseClassMetadata $classMetadata,
103
        DocumentPersister $documentPersister,
104
        ReflectionProperty $reflectionId
105
    ) {
106 105
        if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
107 View Code Duplication
            return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
108
                $proxy->__setInitializer(null);
109
                $proxy->__setCloner(null);
110
111
                if ($proxy->__isInitialized()) {
112
                    return;
113
                }
114
115
                $properties = $proxy->__getLazyProperties();
116
117
                foreach ($properties as $propertyName => $property) {
118
                    if (isset($proxy->$propertyName)) {
119
                        continue;
120
                    }
121
122
                    $proxy->$propertyName = $properties[$propertyName];
123
                }
124
125
                $proxy->__setInitialized(true);
126
                $proxy->__wakeup();
0 ignored issues
show
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...
127
128
                $id = $reflectionId->getValue($proxy);
129
130
                if ($documentPersister->load(['_id' => $id], $proxy) === null) {
131
                    if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
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...
132
                        throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
133
                    }
134
                }
135
136
                if (! ($proxy instanceof NotifyPropertyChanged)) {
137
                    return;
138
                }
139
140
                $proxy->addPropertyChangedListener($this->uow);
141
            };
142
        }
143
144 View Code Duplication
        return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
145 35
            $proxy->__setInitializer(null);
146 35
            $proxy->__setCloner(null);
147
148 35
            if ($proxy->__isInitialized()) {
149
                return;
150
            }
151
152 35
            $properties = $proxy->__getLazyProperties();
153
154 35
            foreach ($properties as $propertyName => $property) {
155 12
                if (isset($proxy->$propertyName)) {
156
                    continue;
157
                }
158
159 12
                $proxy->$propertyName = $properties[$propertyName];
160
            }
161
162 35
            $proxy->__setInitialized(true);
163
164 35
            $id = $reflectionId->getValue($proxy);
165
166 35
            if ($documentPersister->load(['_id' => $id], $proxy) === null) {
167 9
                if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
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 29
            if (! ($proxy instanceof NotifyPropertyChanged)) {
173 28
                return;
174
            }
175
176 1
            $proxy->addPropertyChangedListener($this->uow);
177 105
        };
178
    }
179
180
    /**
181
     * Generates a closure capable of finalizing a cloned proxy
182
     *
183
     * @return Closure
184
     *
185
     * @throws DocumentNotFoundException
186
     */
187 105
    private function createCloner(
188
        BaseClassMetadata $classMetadata,
189
        DocumentPersister $documentPersister,
190
        ReflectionProperty $reflectionId
191
    ) {
192
        return function (BaseProxy $proxy) use ($documentPersister, $classMetadata, $reflectionId) {
193
            if ($proxy->__isInitialized()) {
194
                return;
195
            }
196
197
            $proxy->__setInitialized(true);
198
            $proxy->__setInitializer(null);
199
200
            $id       = $reflectionId->getValue($proxy);
201
            $original = $documentPersister->load(['_id' => $id]);
202
203
            if ($original === null) {
204
                if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
0 ignored issues
show
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...
205
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
206
                }
207
            }
208
209
            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
210
                $propertyName = $reflectionProperty->getName();
211
212
                if (! $classMetadata->hasField($propertyName) && ! $classMetadata->hasAssociation($propertyName)) {
213
                    continue;
214
                }
215
216
                $reflectionProperty->setAccessible(true);
217
                $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
218
            }
219 105
        };
220
    }
221
}
222