Completed
Pull Request — master (#1219)
by Maciej
09:29 queued 03:09
created

DefaultPersistentCollectionGenerator::loadClass()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 39
Code Lines 26

Duplication

Lines 24
Ratio 61.54 %

Code Coverage

Tests 14
CRAP Score 18.0422

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 24
loc 39
ccs 14
cts 27
cp 0.5185
rs 4.9091
cc 9
eloc 26
nc 9
nop 2
crap 18.0422
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\PersistentCollection;
4
5
use Doctrine\ODM\MongoDB\Configuration;
6
7
final class DefaultPersistentCollectionGenerator implements PersistentCollectionGenerator
8
{
9
    /**
10
     * The namespace that contains all persistent collection classes.
11
     *
12
     * @var string
13
     */
14
    private $collectionNamespace;
15
16
    /**
17
     * The directory that contains all persistent collection classes.
18
     *
19
     * @var string
20
     */
21
    private $collectionDir;
22
23
    /**
24
     * @param string $collectionDir
25
     * @param string $collectionNs
26
     */
27 2
    public function __construct($collectionDir, $collectionNs)
28
    {
29 2
        $this->collectionDir = $collectionDir;
30 2
        $this->collectionNamespace = $collectionNs;
31 2
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function generateClass($class, $dir)
37
    {
38
        $collClassName = str_replace('\\', '', $class) . 'Persistent';
39
        $className = $this->collectionNamespace . '\\' . $collClassName;
40
        $fileName = $dir . DIRECTORY_SEPARATOR . $collClassName . '.php';
41
        $this->generateCollectionClass($class, $className, $fileName);
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 2
    public function loadClass($collectionClass, $autoGenerate)
48
    {
49
        // These checks are not in __construct() because of BC and should be moved for 2.0
50 2
        if ( ! $this->collectionDir) {
51
            throw PersistentCollectionException::directoryRequired();
52
        }
53 2
        if ( ! $this->collectionNamespace) {
54
            throw PersistentCollectionException::namespaceRequired();
55
        }
56
57 2
        $collClassName = str_replace('\\', '', $collectionClass) . 'Persistent';
58 2
        $className = $this->collectionNamespace . '\\' . $collClassName;
59 2 View Code Duplication
        if ( ! class_exists($className, false)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60 1
            $fileName = $this->collectionDir . DIRECTORY_SEPARATOR . $collClassName . '.php';
61
            switch ($autoGenerate) {
62 1
                case Configuration::AUTOGENERATE_NEVER:
63
                    require $fileName;
64
                    break;
65
66 1
                case Configuration::AUTOGENERATE_ALWAYS:
67 1
                    $this->generateCollectionClass($collectionClass, $className, $fileName);
68 1
                    require $fileName;
69 1
                    break;
70
71
                case Configuration::AUTOGENERATE_FILE_NOT_EXISTS:
72
                    if ( ! file_exists($fileName)) {
73
                        $this->generateCollectionClass($collectionClass, $className, $fileName);
74
                    }
75
                    require $fileName;
76
                    break;
77
78
                case Configuration::AUTOGENERATE_EVAL:
79
                    $this->generateCollectionClass($collectionClass, $className, false);
80
                    break;
81
            }
82 1
        }
83
84 2
        return $className;
85
    }
86
87 1
    private function generateCollectionClass($for, $targetFqcn, $fileName)
88
    {
89 1
        $exploded = explode('\\', $targetFqcn);
90 1
        $class = array_pop($exploded);
91 1
        $namespace = join('\\', $exploded);
92
        $code = <<<CODE
93
<?php
94
95
namespace $namespace;
96
97
use Doctrine\Common\Collections\Collection as BaseCollection;
98
use Doctrine\ODM\MongoDB\DocumentManager;
99
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
100
use Doctrine\ODM\MongoDB\MongoDBException;
101
use Doctrine\ODM\MongoDB\UnitOfWork;
102
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
103
104
/**
105
 * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PERSISTENT COLLECTION GENERATOR
106
 */
107 1
class $class extends \\$for implements \\Doctrine\\ODM\\MongoDB\\PersistentCollection\\PersistentCollectionInterface
108
{
109
    use \\Doctrine\\ODM\\MongoDB\\PersistentCollection\\PersistentCollectionTrait;
110
111
    /**
112
     * @param BaseCollection \$coll
113
     * @param DocumentManager \$dm
114
     * @param UnitOfWork \$uow
115
     */
116
    public function __construct(BaseCollection \$coll, DocumentManager \$dm, UnitOfWork \$uow)
117
    {
118
        \$this->coll = \$coll;
119
        \$this->dm = \$dm;
120
        \$this->uow = \$uow;
121
    }
122
123 1
CODE;
124 1
        $rc = new \ReflectionClass($for);
125 1
        $rt = new \ReflectionClass('Doctrine\\ODM\\MongoDB\\PersistentCollection\\PersistentCollectionTrait');
126 1
        foreach ($rc->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
127
            if (
128 1
                $rt->hasMethod($method->getName()) ||
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
129 1
                $method->isConstructor() ||
130 1
                $method->isFinal() ||
131 1
                $method->isStatic()
132 1
            ) {
133 1
                continue;
134
            }
135 1
            $code .= $this->generateMethod($method);
136 1
        }
137 1
        $code .= "}\n";
138
139 1
        if ($fileName === false) {
140
            if ( ! class_exists($targetFqcn)) {
141
                eval(substr($code, 5));
142
            }
143 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144 1
            $parentDirectory = dirname($fileName);
145
146 1
            if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) {
147
                throw PersistentCollectionException::directoryNotWritable();
148
            }
149
150 1
            if ( ! is_writable($parentDirectory)) {
151
                throw PersistentCollectionException::directoryNotWritable();
152
            }
153
154 1
            $tmpFileName = $fileName . '.' . uniqid('', true);
155 1
            file_put_contents($tmpFileName, $code);
156 1
            rename($tmpFileName, $fileName);
157
        }
158 1
    }
159
160 1
    private function generateMethod(\ReflectionMethod $method)
161
    {
162 1
        $parametersString = $this->buildParametersString($method);
163 1
        $callParamsString = implode(', ', $this->getParameterNamesForDecoratedCall($method->getParameters()));
164
165
        $method = <<<CODE
166
167
    /**
168
     * {@inheritDoc}
169
     */
170 1
    public function {$method->getName()}($parametersString)
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
171
    {
172
        \$this->initialize();
173 1
        return \$this->coll->{$method->getName()}($callParamsString);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
174
    }
175
176 1
CODE;
177 1
        return $method;
178
    }
179
180
    /**
181
     * @param \ReflectionMethod $method
182
     *
183
     * @return string
184
     */
185 1
    private function buildParametersString(\ReflectionMethod $method)
186
    {
187 1
        $parameters = $method->getParameters();
188 1
        $parameterDefinitions = array();
189
190
        /* @var $param \ReflectionParameter */
191 1
        foreach ($parameters as $param) {
192 1
            $parameterDefinition = '';
193
194 1
            if ($parameterType = $this->getParameterType($param)) {
0 ignored issues
show
Bug introduced by
It seems like $param defined by $param on line 191 can also be of type string; however, Doctrine\ODM\MongoDB\Per...tor::getParameterType() does only seem to accept object<ReflectionParameter>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
195 1
                $parameterDefinition .= $parameterType . ' ';
196 1
            }
197
198 1
            if ($param->isPassedByReference()) {
199
                $parameterDefinition .= '&';
200
            }
201
202 1
            if (method_exists($param, 'isVariadic')) {
203 1
                if ($param->isVariadic()) {
204
                    $parameterDefinition .= '...';
205
                }
206 1
            }
207
208 1
            $parameters[]     = '$' . $param->getName();
209 1
            $parameterDefinition .= '$' . $param->getName();
210
211 1
            if ($param->isDefaultValueAvailable()) {
212
                $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true);
213
            }
214
215 1
            $parameterDefinitions[] = $parameterDefinition;
216 1
        }
217
218 1
        return implode(', ', $parameterDefinitions);
219
    }
220
221
    /**
222
     * @param \ReflectionParameter $parameter
223
     *
224
     * @return string|null
225
     */
226 1
    private function getParameterType(\ReflectionParameter $parameter)
227
    {
228
        // We need to pick the type hint class too
229 1
        if ($parameter->isArray()) {
230
            return 'array';
231
        }
232
233 1
        if (method_exists($parameter, 'isCallable') && $parameter->isCallable()) {
234
            return 'callable';
235
        }
236
237
        try {
238 1
            $parameterClass = $parameter->getClass();
239
240 1
            if ($parameterClass) {
241 1
                return '\\' . $parameterClass->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameterClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
242
            }
243 1
        } catch (\ReflectionException $previous) {
244
            // @todo ProxyGenerator throws specialized exceptions
245
            throw $previous;
246
        }
247
248 1
        return null;
249
    }
250
251
    /**
252
     * @param \ReflectionParameter[] $parameters
253
     *
254
     * @return string[]
255
     */
256 1
    private function getParameterNamesForDecoratedCall(array $parameters)
257
    {
258 1
        return array_map(
259 1
            function (\ReflectionParameter $parameter) {
260 1
                $name = '';
261
262 1
                if (method_exists($parameter, 'isVariadic')) {
263 1
                    if ($parameter->isVariadic()) {
264
                        $name .= '...';
265
                    }
266 1
                }
267
268 1
                $name .= '$' . $parameter->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
269
270 1
                return $name;
271 1
            },
272
            $parameters
273 1
        );
274
    }
275
}
276