Completed
Pull Request — master (#154)
by
unknown
03:41
created

ProxyFactory::getProxy()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 4
nc 6
nop 2
1
<?php
2
3
namespace Doctrine\ODM\CouchDB\Proxy;
4
5
use Doctrine\ODM\CouchDB\DocumentManager;
6
use Doctrine\ODM\CouchDB\Mapping\ClassMetadata;
7
use Doctrine\Common\Util\ClassUtils;
8
9
/**
10
 * This factory is used to create proxy objects for entities at runtime.
11
 *
12
 * @author Roman Borschel <[email protected]>
13
 * @author Giorgio Sironi <[email protected]>
14
 * @author Nils Adermann <[email protected]>
15
 * @author Aurelien Richaud <[email protected]>
16
 *
17
 * This whole thing is copy & pasted from ORM - should really be slightly
18
 * refactored to generate
19
 */
20
class ProxyFactory
21
{
22
    /** The DocumentManager this factory is bound to. */
23
    private $dm;
24
    /** Whether to automatically (re)generate proxy classes. */
25
    private $autoGenerate;
26
    /** The namespace that contains all proxy classes. */
27
    private $proxyNamespace;
28
    /** The directory that contains all proxy classes. */
29
    private $proxyDir;
30
31
    /**
32
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
33
     * connected to the given <tt>DocumentManager</tt>.
34
     *
35
     * @param DocumentManager $dm The DocumentManager the new factory works for.
36
     * @param string $proxyDir The directory to use for the proxy classes. It must exist.
37
     * @param string $proxyNs The namespace to use for the proxy classes.
38
     * @param boolean $autoGenerate Whether to automatically generate proxy classes.
39
     * @throws ProxyException
40
     */
41
    public function __construct(DocumentManager $dm, $proxyDir, $proxyNs, $autoGenerate = false)
42
    {
43
        if ( ! $proxyDir) {
44
            throw ProxyException::proxyDirectoryRequired();
45
        }
46
        if ( ! $proxyNs) {
47
            throw ProxyException::proxyNamespaceRequired();
48
        }
49
        $this->dm = $dm;
50
        $this->proxyDir = $proxyDir;
51
        $this->autoGenerate = $autoGenerate;
52
        $this->proxyNamespace = $proxyNs;
53
    }
54
55
    /**
56
     * Gets a reference proxy instance for the entity of the given type and identified by
57
     * the given identifier.
58
     *
59
     * @param string $className
60
     * @param mixed $identifier
61
     * @return object
62
     */
63
    public function getProxy($className, $identifier)
64
    {
65
        $fqn = ClassUtils::generateProxyClassName($className, $this->proxyNamespace);
66
67
        if ( ! class_exists($fqn, false)) {
68
            $fileName = $this->getProxyFileName($className);
69
            if ($this->autoGenerate) {
70
                $this->generateProxyClass($this->dm->getClassMetadata($className), $fileName, self::$proxyClassTemplate);
71
            }
72
            require $fileName;
73
        }
74
75
        if ( ! $this->dm->getMetadataFactory()->hasMetadataFor($fqn)) {
76
            $this->dm->getMetadataFactory()->setMetadataFor($fqn, $this->dm->getClassMetadata($className));
77
        }
78
79
        return new $fqn($this->dm, $identifier);
80
    }
81
82
    /**
83
     * Generate the Proxy file name
84
     *
85
     * @param string $className
86
     * @param string $baseDir Optional base directory for proxy file name generation.
87
     *                        If not specified, the directory configured on the Configuration of the
88
     *                        EntityManager will be used by this factory.
89
     * @return string
90
     */
91
    private function getProxyFileName($className, $baseDir = null)
92
    {
93
        $proxyDir = $baseDir ?: $this->proxyDir;
94
95
        return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
96
    }
97
98
    /**
99
     * Generates proxy classes for all given classes.
100
     *
101
     * @param array $classes The classes (ClassMetadata instances) for which to generate proxies.
102
     * @param string $toDir The target directory of the proxy classes. If not specified, the
103
     *                      directory configured on the Configuration of the DocumentManager used
104
     *                      by this factory is used.
105
     */
106
    public function generateProxyClasses(array $classes, $toDir = null)
107
    {
108
        $proxyDir = $toDir ?: $this->proxyDir;
109
        $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
0 ignored issues
show
Unused Code introduced by
$proxyDir is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
110
        foreach ($classes as $class) {
111
            /* @var $class ClassMetadata */
112
            if ($class->isMappedSuperclass) {
113
                continue;
114
            }
115
116
            $proxyFileName = $this->getProxyFileName($class->name, $toDir);
117
            $this->generateProxyClass($class, $proxyFileName, self::$proxyClassTemplate);
118
        }
119
    }
120
121
    /**
122
     * Generates a proxy class file.
123
     *
124
     * @param $class
125
     * @param $fileName
126
     * @param $template
127
     */
128
    private function generateProxyClass($class, $fileName, $template)
129
    {
130
        $methods = $this->generateMethods($class);
131
        $sleepImpl = $this->generateSleep($class);
132
133
        $placeholders = array(
134
            '<namespace>',
135
            '<proxyClassName>', '<className>',
136
            '<methods>', '<sleepImpl>'
137
        );
138
139
        $className = ltrim($class->name, '\\');
140
        $proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->proxyNamespace);
141
        $parts = explode('\\', strrev($proxyClassName), 2);
142
        $proxyClassNamespace = strrev($parts[1]);
143
        $proxyClassName = strrev($parts[0]);
144
145
        $replacements = array(
146
            $proxyClassNamespace,
147
            $proxyClassName,
148
            $className,
149
            $methods,
150
            $sleepImpl
151
        );
152
153
        $template = str_replace($placeholders, $replacements, $template);
154
155
        file_put_contents($fileName, $template, LOCK_EX);
156
    }
157
158
    /**
159
     * Generates the methods of a proxy class.
160
     *
161
     * @param ClassMetadata $class
162
     * @return string The code of the generated methods.
163
     */
164
    private function generateMethods(ClassMetadata $class)
165
    {
166
        $methods = '';
167
168
        foreach ($class->reflClass->getMethods() as $method) {
169
            /* @var $method \ReflectionMethod */
170
            if ($method->isConstructor() || strtolower($method->getName()) == "__sleep") {
171
                continue;
172
            }
173
174
            if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
175
                $methods .= PHP_EOL . '    public function ';
176
                if ($method->returnsReference()) {
177
                    $methods .= '&';
178
                }
179
                $methods .= $method->getName() . '(';
180
                $firstParam = true;
181
                $parameterString = $argumentString = '';
182
183
                foreach ($method->getParameters() as $param) {
184
                    if ($firstParam) {
185
                        $firstParam = false;
186
                    } else {
187
                        $parameterString .= ', ';
188
                        $argumentString  .= ', ';
189
                    }
190
191
	                // We need to pick the type hint class too
192
	                if (($paramClass = $param->getClass()) !== null) {
193
		                $parameterString .= ($param->allowsNull() ? '?' : '').'\\'.$paramClass->getName().' ';
194
	                } elseif (method_exists($param, 'getType') && $param->getType()) {
195
		                $parameterString .= ($param->getType()->allowsNull() ? '?' : '').$param->getType()->getName().' ';
196
	                } elseif ($param->isArray()) {
197
		                $parameterString .= 'array ';
198
	                }
199
200
                    if ($param->isPassedByReference()) {
201
                        $parameterString .= '&';
202
                    }
203
204
                    $parameterString .= '$' . $param->getName();
205
                    $argumentString  .= '$' . $param->getName();
206
207
                    if ($param->isDefaultValueAvailable()) {
208
                        $parameterString .= ' = ' . var_export($param->getDefaultValue(), true);
209
                    }
210
                }
211
212
                $methods .= $parameterString . ')';
213
	            if ( method_exists($method, 'getReturnType') ) {
214
		            $returnType = $method->getReturnType();
215
		            if ( $returnType ) {
216
			            $returnName = ( class_exists( $returnType->getName() ) ? '\\' : '' ) . $returnType->getName();
217
			            $methods    .= ': ' . ( $returnType->allowsNull() ? '?' : '' ) . $returnName;
218
		            }
219
	            }
220
                $methods .= PHP_EOL . '    {' . PHP_EOL;
221
                $methods .= '        $this->__load();' . PHP_EOL;
222
                $methods .= '        return parent::' . $method->getName() . '(' . $argumentString . ');';
223
                $methods .= PHP_EOL . '    }' . PHP_EOL;
224
            }
225
        }
226
227
        return $methods;
228
    }
229
230
    /**
231
     * Generates the code for the __sleep method for a proxy class.
232
     *
233
     * @param $class
234
     * @return string
235
     */
236
    private function generateSleep(ClassMetadata $class)
237
    {
238
        $sleepImpl = '';
239
240
        if ($class->reflClass->hasMethod('__sleep')) {
241
            $sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());";
242
        } else {
243
            $sleepImpl .= "return array('__isInitialized__', ";
244
245
            $properties = array();
246
            foreach ($class->fieldMappings as $name => $prop) {
247
                $properties[] = "'$name'";
248
            }
249
250
            $sleepImpl .= implode(',', $properties) . ');';
251
        }
252
253
        return $sleepImpl;
254
    }
255
256
    /** Proxy class code template */
257
    private static $proxyClassTemplate = <<<'PHP'
258
<?php
259
260
namespace <namespace>;
261
262
/**
263
 * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
264
 */
265
class <proxyClassName> extends \<className> implements \Doctrine\ODM\CouchDB\Proxy\Proxy
266
{
267
    private $__doctrineDocumentManager__;
268
    private $__doctrineIdentifier__;
269
    public $__isInitialized__ = false;
270
    public function __construct($documentManager, $identifier)
271
    {
272
        $this->__doctrineDocumentManager__ = $documentManager;
273
        $this->__doctrineIdentifier__ = $identifier;
274
    }
275
    public function __load()
276
    {
277
        if (!$this->__isInitialized__ && $this->__doctrineDocumentManager__) {
278
            $this->__isInitialized__ = true;
279
            $this->__doctrineDocumentManager__->refresh($this);
280
            unset($this->__doctrineDocumentManager__, $this->__doctrineIdentifier__);
281
        }
282
    }
283
284
    public function __isInitialized()
285
    {
286
        return $this->__isInitialized__;
287
    }
288
289
    <methods>
290
291
    public function __sleep()
292
    {
293
        <sleepImpl>
294
    }
295
}
296
PHP;
297
}
298
299