Completed
Push — master ( 4b5952...b8f7dd )
by Andreas
11s
created

StaticProxyFactory   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 79.59%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 7
dl 0
loc 126
ccs 39
cts 49
cp 0.7959
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getProxy() 0 18 1
A generateProxyClasses() 0 20 4
A createInitializer() 0 35 4
A skippedFieldsFqns() 0 10 2
A propertyFqcn() 0 12 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Proxy\Factory;
6
7
use Closure;
8
use Doctrine\Common\EventManager;
9
use Doctrine\Common\NotifyPropertyChanged;
10
use Doctrine\ODM\MongoDB\DocumentManager;
11
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
12
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
13
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
14
use Doctrine\ODM\MongoDB\UnitOfWork;
15
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
16
use ProxyManager\Factory\LazyLoadingGhostFactory;
17
use ProxyManager\Proxy\GhostObjectInterface;
18
use ReflectionProperty;
19
use function array_filter;
20
use function count;
21
22
/**
23
 * This factory is used to create proxy objects for documents at runtime.
24
 */
25
class StaticProxyFactory implements ProxyFactory
26
{
27
    /** @var UnitOfWork The UnitOfWork this factory is bound to. */
28
    private $uow;
29
30
    /** @var EventManager */
31
    private $lifecycleEventManager;
32
33
    /** @var LazyLoadingGhostFactory */
34
    private $proxyFactory;
35
36 1634
    public function __construct(DocumentManager $documentManager)
37
    {
38 1634
        $this->uow                   = $documentManager->getUnitOfWork();
39 1634
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
0 ignored issues
show
Documentation Bug introduced by Andreas Braun
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...
40 1634
        $this->proxyFactory          = $documentManager->getConfiguration()->buildGhostObjectFactory();
41 1634
    }
42
43 104
    public function getProxy(ClassMetadata $metadata, $identifier) : GhostObjectInterface
44
    {
45 104
        $documentPersister = $this->uow->getDocumentPersister($metadata->getName());
46
47
        $ghostObject = $this
48 104
            ->proxyFactory
49 104
            ->createProxy(
50 104
                $metadata->getName(),
51 104
                $this->createInitializer($metadata, $documentPersister),
52
                [
53 104
                    'skippedProperties' => $this->skippedFieldsFqns($metadata),
54
                ]
55
            );
56
57 104
        $metadata->setIdentifierValue($ghostObject, $identifier);
0 ignored issues
show
Documentation introduced by Andreas Braun
$ghostObject is of type object<ProxyManager\Proxy\GhostObjectInterface>, but the function expects a object<Doctrine\ODM\MongoDB\Mapping\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
59 104
        return $ghostObject;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     *
65
     * @param ClassMetadata[] $classes
66
     */
67
    public function generateProxyClasses(array $classes) : int
68
    {
69
        $concreteClasses = array_filter($classes, static function (ClassMetadata $metadata) : bool {
70
            return ! ($metadata->isMappedSuperclass || $metadata->isQueryResultDocument || $metadata->getReflectionClass()->isAbstract());
71
        });
72
73
        foreach ($concreteClasses as $metadata) {
74
            $this
75
                ->proxyFactory
76
                ->createProxy(
77
                    $metadata->getName(),
78
                    static function () {
79
                        // empty closure, serves its purpose, for now
80
                    },
81
                    $this->skippedFieldsFqns($metadata)
82
                );
83
        }
84
85
        return count($concreteClasses);
86
    }
87
88 104
    private function createInitializer(
89
        ClassMetadata $metadata,
90
        DocumentPersister $documentPersister
91
    ) : Closure {
92
        return function (
93
            GhostObjectInterface $ghostObject,
94
            string $method,
95
            // we don't care
96
            array $parameters,
97
            // we don't care
98
            & $initializer,
99
            array $properties // we currently do not use this
0 ignored issues
show
Unused Code introduced by Andreas Braun
The parameter $properties is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
100
        ) use (
101 36
            $metadata,
102 36
            $documentPersister
103
        ) : bool {
104 36
            $originalInitializer = $initializer;
105 36
            $initializer         = null;
106 36
            $identifier          = $metadata->getIdentifierValue($ghostObject);
0 ignored issues
show
Documentation introduced by Andreas Braun
$ghostObject is of type object<ProxyManager\Proxy\GhostObjectInterface>, but the function expects a object<Doctrine\ODM\MongoDB\Mapping\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
108 36
            if (! $documentPersister->load(['_id' => $identifier], $ghostObject)) {
109 9
                $initializer = $originalInitializer;
110
111 9
                if (! $this->lifecycleEventManager->documentNotFound($ghostObject, $identifier)) {
0 ignored issues
show
Bug introduced by Andreas Braun
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...
112 8
                    throw DocumentNotFoundException::documentNotFound($metadata->getName(), $identifier);
113
                }
114
            }
115
116 30
            if ($ghostObject instanceof NotifyPropertyChanged) {
117 1
                $ghostObject->addPropertyChangedListener($this->uow);
118
            }
119
120 30
            return true;
121 104
        };
122
    }
123
124
    /**
125
     * @return string[]
126
     */
127 104
    private function skippedFieldsFqns(ClassMetadata $metadata) : array
128
    {
129 104
        $idFieldFqcns = [];
130
131 104
        foreach ($metadata->getIdentifierFieldNames() as $idField) {
132 104
            $idFieldFqcns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField));
133
        }
134
135 104
        return $idFieldFqcns;
136
    }
137
138 104
    private function propertyFqcn(ReflectionProperty $property) : string
139
    {
140 104
        if ($property->isPrivate()) {
141 50
            return "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
0 ignored issues
show
introduced by Andreas Braun
Consider using $property->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
142
        }
143
144 60
        if ($property->isProtected()) {
145 22
            return "\0*\0" . $property->getName();
146
        }
147
148 38
        return $property->getName();
149
    }
150
}
151