Completed
Push — master ( 7b9f4b...1cd743 )
by Andreas
13s queued 10s
created

ODM/MongoDB/Mapping/Driver/AnnotationDriver.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\Reader;
9
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
10
use Doctrine\ODM\MongoDB\Events;
11
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
12
use Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractIndex;
13
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
14
use Doctrine\ODM\MongoDB\Mapping\MappingException;
15
use ReflectionClass;
16
use ReflectionMethod;
17
use const E_USER_DEPRECATED;
18
use function array_merge;
19
use function array_replace;
20
use function assert;
21
use function constant;
22
use function get_class;
23
use function is_array;
24
use function trigger_error;
25
26
/**
27
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
28
 */
29
class AnnotationDriver extends AbstractAnnotationDriver
30
{
31 963
    public function isTransient($className)
32
    {
33 963
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
34
35 963
        foreach ($classAnnotations as $annot) {
36 962
            if ($annot instanceof ODM\AbstractDocument) {
37 962
                return false;
38
            }
39
        }
40 112
        return true;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 1493
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class) : void
47
    {
48 1493
        assert($class instanceof ClassMetadata);
49 1493
        $reflClass = $class->getReflectionClass();
50
51 1493
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
52
53 1493
        $documentAnnot = null;
54 1493
        foreach ($classAnnotations as $annot) {
55 1491
            $classAnnotations[get_class($annot)] = $annot;
56
57 1491
            if ($annot instanceof ODM\AbstractDocument) {
58 1491
                if ($documentAnnot !== null) {
59 5
                    throw MappingException::classCanOnlyBeMappedByOneAbstractDocument($className, $documentAnnot, $annot);
60
                }
61 1491
                $documentAnnot = $annot;
62
            }
63
64
            // non-document class annotations
65 1491
            if ($annot instanceof ODM\AbstractIndex) {
66 6
                $this->addIndex($class, $annot);
67
            }
68 1491
            if ($annot instanceof ODM\Indexes) {
69
                // Setting the type to mixed is a workaround until https://github.com/doctrine/annotations/pull/209 is released.
70
                /** @var mixed $value */
71 137
                $value = $annot->value;
72 137
                foreach (is_array($value) ? $value : [$value] as $index) {
73 137
                    $this->addIndex($class, $index);
74
                }
75 1491
            } elseif ($annot instanceof ODM\InheritanceType) {
76 904
                $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $annot->value));
77 1491
            } elseif ($annot instanceof ODM\DiscriminatorField) {
78 175
                $class->setDiscriminatorField($annot->value);
79 1491
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
80
                /** @var array $value */
81 174
                $value = $annot->value;
82 174
                $class->setDiscriminatorMap($value);
83 1491
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
84
                $class->setDiscriminatorValue($annot->value);
85 1491
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
86 109
                $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . $annot->value));
87 1491
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
88 111
                $class->setDefaultDiscriminatorValue($annot->value);
89 1491
            } elseif ($annot instanceof ODM\ReadPreference) {
90 6
                $class->setReadPreference($annot->value, $annot->tags ?? []);
91
            }
92
        }
93
94 1488
        if ($documentAnnot === null) {
95 3
            throw MappingException::classIsNotAValidDocument($className);
96
        }
97
98 1486
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
99 894
            $class->isMappedSuperclass = true;
100 1485
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
101 330
            $class->isEmbeddedDocument = true;
102 1478
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
103 110
            $class->isQueryResultDocument = true;
104 1478
        } elseif ($documentAnnot instanceof ODM\File) {
105 122
            $class->isFile = true;
106
107 122
            if ($documentAnnot->chunkSizeBytes !== null) {
108 121
                $class->setChunkSizeBytes($documentAnnot->chunkSizeBytes);
109
            }
110
        }
111
112 1486
        if (isset($documentAnnot->db)) {
113 1
            $class->setDatabase($documentAnnot->db);
0 ignored issues
show
The property db does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
114
        }
115 1486
        if (isset($documentAnnot->collection)) {
116 956
            $class->setCollection($documentAnnot->collection);
0 ignored issues
show
The property collection does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
117
        }
118
        // Store bucketName as collection name for GridFS files
119 1486
        if (isset($documentAnnot->bucketName)) {
120 1
            $class->setBucketName($documentAnnot->bucketName);
0 ignored issues
show
The property bucketName does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
121
        }
122 1486
        if (isset($documentAnnot->repositoryClass)) {
123 120
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
0 ignored issues
show
The property repositoryClass does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
124
        }
125 1486
        if (isset($documentAnnot->writeConcern)) {
126 10
            $class->setWriteConcern($documentAnnot->writeConcern);
0 ignored issues
show
The property writeConcern does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
127
        }
128 1486
        if (isset($documentAnnot->indexes)) {
129 1485
            foreach ($documentAnnot->indexes as $index) {
0 ignored issues
show
The property indexes does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
130
                $this->addIndex($class, $index);
131
            }
132
        }
133 1486
        if (! empty($documentAnnot->readOnly)) {
0 ignored issues
show
The property readOnly does not exist on object<Doctrine\ODM\Mong...tions\AbstractDocument>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
134 5
            $class->markReadOnly();
135
        }
136
137 1486
        foreach ($reflClass->getProperties() as $property) {
138 1485
            if (($class->isMappedSuperclass && ! $property->isPrivate())
139
                ||
140 1485
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
141 936
                continue;
142
            }
143
144 1484
            $indexes    = [];
145 1484
            $mapping    = ['fieldName' => $property->getName()];
146 1484
            $fieldAnnot = null;
147
148 1484
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
149 1484
                if ($annot instanceof ODM\AbstractField) {
150 1484
                    $fieldAnnot = $annot;
151 1484
                    if ($annot->isDeprecated()) {
152
                        @trigger_error($annot->getDeprecationMessage(), E_USER_DEPRECATED);
153
                    }
154
                }
155 1484
                if ($annot instanceof ODM\AbstractIndex) {
156 225
                    $indexes[] = $annot;
157
                }
158 1484
                if ($annot instanceof ODM\Indexes) {
159
                    // Setting the type to mixed is a workaround until https://github.com/doctrine/annotations/pull/209 is released.
160
                    /** @var mixed $value */
161
                    $value = $annot->value;
162
                    foreach (is_array($value) ? $value : [$value] as $index) {
163
                        $indexes[] = $index;
164
                    }
165 1484
                } elseif ($annot instanceof ODM\AlsoLoad) {
166 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
167 1484
                } elseif ($annot instanceof ODM\Version) {
168 151
                    $mapping['version'] = true;
169 1484
                } elseif ($annot instanceof ODM\Lock) {
170 22
                    $mapping['lock'] = true;
171
                }
172
            }
173
174 1484
            if ($fieldAnnot) {
175 1484
                $mapping = array_replace($mapping, (array) $fieldAnnot);
176 1484
                $class->mapField($mapping);
177
            }
178
179 1484
            if (! $indexes) {
180 1484
                continue;
181
            }
182
183 225
            foreach ($indexes as $index) {
184 225
                $name = $mapping['name'] ?? $mapping['fieldName'];
185 225
                $keys = [$name => $index->order ?: 'asc'];
186 225
                $this->addIndex($class, $index, $keys);
187
            }
188
        }
189
190
        // Set shard key after all fields to ensure we mapped all its keys
191 1483
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
192 131
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
193
        }
194
195
        /** @var ReflectionMethod $method */
196 1482
        foreach ($reflClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
197
            /* Filter for the declaring class only. Callbacks from parent
198
             * classes will already be registered.
199
             */
200 1230
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
201 901
                continue;
202
            }
203
204 1230
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
205 895
                if ($annot instanceof ODM\AlsoLoad) {
206 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
207
                }
208
209 895
                if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
210 127
                    continue;
211
                }
212
213 876
                if ($annot instanceof ODM\PrePersist) {
214 857
                    $class->addLifecycleCallback($method->getName(), Events::prePersist);
215 138
                } elseif ($annot instanceof ODM\PostPersist) {
216 10
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
217 138
                } elseif ($annot instanceof ODM\PreUpdate) {
218 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
219 133
                } elseif ($annot instanceof ODM\PostUpdate) {
220 122
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
221 127
                } elseif ($annot instanceof ODM\PreRemove) {
222 125
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
223 127
                } elseif ($annot instanceof ODM\PostRemove) {
224 125
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
225 127
                } elseif ($annot instanceof ODM\PreLoad) {
226 126
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
227 126
                } elseif ($annot instanceof ODM\PostLoad) {
228 125
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
229 11
                } elseif ($annot instanceof ODM\PreFlush) {
230 11
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
231
                }
232
            }
233
        }
234 1482
    }
235
236 257
    private function addIndex(ClassMetadata $class, AbstractIndex $index, array $keys = []) : void
237
    {
238 257
        $keys    = array_merge($keys, $index->keys);
239 257
        $options = [];
240 257
        $allowed = ['name', 'background', 'unique', 'sparse', 'expireAfterSeconds'];
241 257
        foreach ($allowed as $name) {
242 257
            if (! isset($index->$name)) {
243 257
                continue;
244
            }
245
246 257
            $options[$name] = $index->$name;
247
        }
248 257
        if (! empty($index->partialFilterExpression)) {
249 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
250
        }
251 257
        $options = array_merge($options, $index->options);
252 257
        $class->addIndex($keys, $options);
253 257
    }
254
255
    /**
256
     * @throws MappingException
257
     */
258 131
    private function setShardKey(ClassMetadata $class, ODM\ShardKey $shardKey) : void
259
    {
260 131
        $options = [];
261 131
        $allowed = ['unique', 'numInitialChunks'];
262 131
        foreach ($allowed as $name) {
263 131
            if (! isset($shardKey->$name)) {
264 131
                continue;
265
            }
266
267
            $options[$name] = $shardKey->$name;
268
        }
269
270 131
        $class->setShardKey($shardKey->keys, $options);
271 130
    }
272
273
    /**
274
     * Factory method for the Annotation Driver
275
     *
276
     * @param string[]|string $paths
277
     */
278 1756
    public static function create($paths = [], ?Reader $reader = null) : AnnotationDriver
279
    {
280 1756
        if ($reader === null) {
281 1756
            $reader = new AnnotationReader();
282
        }
283 1756
        return new self($reader, $paths);
284
    }
285
}
286