Completed
Push — master ( f896ed...f61d90 )
by Andreas
21:33 queued 10s
created

StaticProxyFactory::propertyFqcn()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.8666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 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\NotifyPropertyChanged;
9
use Doctrine\ODM\MongoDB\DocumentManager;
10
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
11
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
12
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
13
use Doctrine\ODM\MongoDB\UnitOfWork;
14
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
15
use ProxyManager\Factory\LazyLoadingGhostFactory;
16
use ProxyManager\Proxy\GhostObjectInterface;
17
use ReflectionProperty;
18
use function array_filter;
19
use function count;
20
21
/**
22
 * This factory is used to create proxy objects for documents at runtime.
23
 */
24
class StaticProxyFactory implements ProxyFactory
25
{
26
    /** @var UnitOfWork The UnitOfWork this factory is bound to. */
27
    private $uow;
28
29
    /** @var LifecycleEventManager */
30
    private $lifecycleEventManager;
31
32
    /** @var LazyLoadingGhostFactory */
33
    private $proxyFactory;
34
35 1658
    public function __construct(DocumentManager $documentManager)
36
    {
37 1658
        $this->uow                   = $documentManager->getUnitOfWork();
38 1658
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
39 1658
        $this->proxyFactory          = $documentManager->getConfiguration()->buildGhostObjectFactory();
40 1658
    }
41
42 99
    public function getProxy(ClassMetadata $metadata, $identifier) : GhostObjectInterface
43
    {
44 99
        $documentPersister = $this->uow->getDocumentPersister($metadata->getName());
0 ignored issues
show
Bug introduced by
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
45
46
        $ghostObject = $this
47 99
            ->proxyFactory
48 99
            ->createProxy(
49 99
                $metadata->getName(),
0 ignored issues
show
Bug introduced by
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
50 99
                $this->createInitializer($metadata, $documentPersister),
51
                [
52 99
                    'skippedProperties' => $this->skippedFieldsFqns($metadata),
53
                ]
54
            );
55
56 99
        $metadata->setIdentifierValue($ghostObject, $identifier);
57
58 99
        return $ghostObject;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     *
64
     * @param ClassMetadata[] $classes
65
     */
66 1
    public function generateProxyClasses(array $classes) : int
67
    {
68
        $concreteClasses = array_filter($classes, static function (ClassMetadata $metadata) : bool {
69 1
            return ! ($metadata->isMappedSuperclass || $metadata->isQueryResultDocument || $metadata->getReflectionClass()->isAbstract());
70 1
        });
71
72 1
        foreach ($concreteClasses as $metadata) {
73
            $this
74 1
                ->proxyFactory
75 1
                ->createProxy(
76 1
                    $metadata->getName(),
77
                    static function () {
78
                        // empty closure, serves its purpose, for now
79 1
                    },
80
                    [
81 1
                        'skippedProperties' => $this->skippedFieldsFqns($metadata),
82
                    ]
83
                );
84
        }
85
86 1
        return count($concreteClasses);
87
    }
88
89 99
    private function createInitializer(
90
        ClassMetadata $metadata,
91
        DocumentPersister $documentPersister
92
    ) : Closure {
93
        return function (
94
            GhostObjectInterface $ghostObject,
95
            string $method,
96
            // we don't care
97
            array $parameters,
98
            // we don't care
99
            & $initializer,
100
            array $properties // we currently do not use this
0 ignored issues
show
Unused Code introduced by
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...
101
        ) use (
102 33
            $metadata,
103 33
            $documentPersister
104
        ) : bool {
105 33
            $originalInitializer = $initializer;
106 33
            $initializer         = null;
107 33
            $identifier          = $metadata->getIdentifierValue($ghostObject);
108
109 33
            if (! $documentPersister->load(['_id' => $identifier], $ghostObject)) {
110 9
                $initializer = $originalInitializer;
111
112 9
                if (! $this->lifecycleEventManager->documentNotFound($ghostObject, $identifier)) {
113 8
                    throw DocumentNotFoundException::documentNotFound($metadata->getName(), $identifier);
0 ignored issues
show
Bug introduced by
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
114
                }
115
            }
116
117 27
            if ($ghostObject instanceof NotifyPropertyChanged) {
0 ignored issues
show
Bug introduced by
The class Doctrine\Common\NotifyPropertyChanged does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
118 1
                $ghostObject->addPropertyChangedListener($this->uow);
119
            }
120
121 27
            return true;
122 99
        };
123
    }
124
125
    /**
126
     * @return string[]
127
     */
128 99
    private function skippedFieldsFqns(ClassMetadata $metadata) : array
129
    {
130 99
        $idFieldFqcns = [];
131
132 99
        foreach ($metadata->getIdentifierFieldNames() as $idField) {
133 99
            $idFieldFqcns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField));
134
        }
135
136 99
        return $idFieldFqcns;
137
    }
138
139 99
    private function propertyFqcn(ReflectionProperty $property) : string
140
    {
141 99
        if ($property->isPrivate()) {
142 51
            return "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
0 ignored issues
show
introduced by
Consider using $property->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
143
        }
144
145 54
        if ($property->isProtected()) {
146 22
            return "\0*\0" . $property->getName();
147
        }
148
149 32
        return $property->getName();
150
    }
151
}
152