Completed
Pull Request — master (#154)
by
unknown
02:14 queued 37s
created

StaticProxyFactory::identifierFieldFqns()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
declare( strict_types=1 );
4
5
namespace Doctrine\ODM\CouchDB\Proxy;
6
7
use Closure;
8
use Doctrine\ODM\CouchDB\DocumentManager;
9
use Doctrine\ODM\CouchDB\Mapping\ClassMetadata;
10
use ProxyManager\Factory\LazyLoadingGhostFactory;
11
use ProxyManager\Proxy\GhostObjectInterface;
12
use ReflectionProperty;
13
14
/**
15
 * Static factory for proxy objects.
16
 *
17
 * @internal this class is to be used by ORM internals only
18
 */
19
final class StaticProxyFactory implements ProxyFactory {
20
	private const SKIPPED_PROPERTIES = 'skippedProperties';
21
22
	/** @var DocumentManager */
23
	private $dm;
24
25
	/** @var LazyLoadingGhostFactory */
26
	private $proxyFactory;
27
28
	/** @var Closure[] indexed by metadata class name */
29
	private $cachedInitializers = [];
30
31
	/** @var string[][][] indexed by metadata class name */
32
	private $cachedSkippedProperties = [];
33
34
	public function __construct(
35
		DocumentManager $dm,
36
		LazyLoadingGhostFactory $proxyFactory
37
	) {
38
		$this->dm           = $dm;
39
		$this->proxyFactory = $proxyFactory;
40
	}
41
42
	/**
43
	 * {@inheritdoc}
44
	 *
45
	 * @param ClassMetadata[] $classes
46
	 */
47
	public function generateProxyClasses( array $classes ): int {
48
		$concreteClasses = array_filter( $classes,
49
			static function ( ClassMetadata $metadata ): bool {
50
				return ! ( $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract() );
51
			} );
52
		foreach ( $concreteClasses as $metadata ) {
53
			$this
54
				->proxyFactory
55
				->createProxy(
56
					$metadata->getName(),
57
					static function () {
58
						// empty closure, serves its purpose, for now
59
					},
60
					$this->skippedFieldsFqns( $metadata )
61
				);
62
		}
63
64
		return count( $concreteClasses );
65
	}
66
67
	/**
68
	 * {@inheritdoc}
69
	 *
70
	 * @param ClassMetadata $metadata
71
	 * @param string $identifier
72
	 *
73
	 * @return GhostObjectInterface
74
	 */
75
	public function getProxy( ClassMetadata $metadata, string $identifier ): GhostObjectInterface {
76
		$className     = $metadata->getName();
77
		$proxyInstance = $this
78
			->proxyFactory
79
			->createProxy(
80
				$metadata->getName(),
81
				$this->cachedInitializers[ $className ]
82
				?? $this->cachedInitializers[ $className ] = $this->makeInitializer( $metadata ),
83
				$this->cachedSkippedProperties[ $className ]
84
				?? $this->cachedSkippedProperties[ $className ] = [
85
					self::SKIPPED_PROPERTIES => $this->skippedFieldsFqns( $metadata ),
86
				]
87
			);
88
		$metadata->setIdentifierValue($proxyInstance, $identifier);
89
90
		if ( $this->dm->getClassMetadataFactory() && ! $this->dm->getClassMetadataFactory()->hasMetadataFor(get_class($proxyInstance))  ) {
91
			$this->dm->getClassMetadataFactory()->setMetadataFor( get_class( $proxyInstance ), $metadata );
92
		}
93
94
		return $proxyInstance;
95
	}
96
97
	/**
98
	 * @param ClassMetadata $metadata
99
	 *
100
	 * @return Closure
101
	 */
102
	private function makeInitializer( ClassMetadata $metadata ): Closure {
103
		$dm = $this->dm;
104
		return static function (
105
			GhostObjectInterface $ghostObject,
106
			string $method,
107
			// we don't care
108
			array $parameters,
109
			// we don't care
110
			& $initializer,
111
			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...
112
		) use (
113
			$metadata,
114
			$dm
115
		) : bool {
116
			$initializer         = null;
117
118
			$dm->refresh($ghostObject);
119
120
			return true;
121
		};
122
	}
123
124
	/**
125
	 * @param ClassMetadata $metadata
126
	 *
127
	 * @return string[]
128
	 */
129
	private function skippedFieldsFqns( ClassMetadata $metadata ): array {
130
		return array_merge(
131
			$this->identifierFieldFqns( $metadata ),
132
			$this->transientFieldsFqns( $metadata )
133
		);
134
	}
135
136
	/**
137
	 * @param ClassMetadata $metadata
138
	 *
139
	 * @return string[]
140
	 */
141
	private function transientFieldsFqns( ClassMetadata $metadata ): array {
0 ignored issues
show
Unused Code introduced by
The parameter $metadata 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...
142
		return [];
143
144
		$transientFieldsFqns = [];
0 ignored issues
show
Unused Code introduced by
$transientFieldsFqns = array(); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
145
		foreach ( $metadata->fieldMappings as $name => $property ) {
146
			// TODO : Transient fields don't have mapping
147
			/*
148
			if ( ! $property instanceof TransientMetadata ) {
149
				continue;
150
			}
151
			$transientFieldsFqns[] = $this->propertyFqcn( $metadata->getReflectionProperty( $name ) );
152
			*/
153
		}
154
155
		return $transientFieldsFqns;
156
	}
157
158
	/**
159
	 * @param ClassMetadata $metadata
160
	 *
161
	 * @return string[]
162
	 */
163
	private function identifierFieldFqns( ClassMetadata $metadata ): array {
164
		$idFieldFqcns = [];
165
		foreach ( $metadata->getIdentifierFieldNames() as $idField ) {
166
			if ( !$idField ) {
167
				continue;
168
			}
169
			$idFieldFqcns[] = $this->propertyFqcn( $metadata->getReflectionProperty( $idField ) );
170
		}
171
172
		return $idFieldFqcns;
173
	}
174
175
	private function propertyFqcn( ReflectionProperty $property ): string {
176
		if ( $property->isPrivate() ) {
177
			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...
178
		}
179
		if ( $property->isProtected() ) {
180
			return "\0*\0" . $property->getName();
181
		}
182
183
		return $property->getName();
184
	}
185
}