Completed
Pull Request — master (#243)
by Guilh
02:34
created

Metadata/Driver/AnnotationDriver.php (1 issue)

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
/*
4
 * Copyright 2011 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace JMS\DiExtraBundle\Metadata\Driver;
20
21
use JMS\DiExtraBundle\Annotation\MetadataProcessorInterface;
22
use JMS\DiExtraBundle\Annotation\SecurityFunction;
23
24
use JMS\DiExtraBundle\Annotation\AfterSetup;
25
26
use JMS\DiExtraBundle\Annotation\FormType;
27
28
use JMS\DiExtraBundle\Annotation\AbstractDoctrineListener;
29
use JMS\DiExtraBundle\Annotation\DoctrineListener;
30
use JMS\DiExtraBundle\Annotation\DoctrineMongoDBListener;
31
use JMS\DiExtraBundle\Annotation\Reference as AnnotReference;
32
use JMS\DiExtraBundle\Annotation\LookupMethod;
33
use JMS\DiExtraBundle\Annotation\Validator;
34
use JMS\DiExtraBundle\Annotation\InjectParams;
35
use JMS\DiExtraBundle\Exception\InvalidTypeException;
36
use JMS\DiExtraBundle\Annotation\Observe;
37
use Doctrine\Common\Annotations\Reader;
38
use JMS\DiExtraBundle\Annotation\Inject;
39
use JMS\DiExtraBundle\Annotation\Service;
40
use JMS\DiExtraBundle\Annotation\Tag;
41
use JMS\DiExtraBundle\Metadata\ClassMetadata;
42
use JMS\DiExtraBundle\Metadata\NamingStrategy;
43
use Metadata\Driver\DriverInterface;
44
use Symfony\Component\DependencyInjection\ContainerInterface;
45
use Symfony\Component\DependencyInjection\Reference;
46
47
class AnnotationDriver implements DriverInterface
48
{
49
    private $reader;
50
    private $namingStrategy;
51
52
    public function __construct(Reader $reader, NamingStrategy $namingStrategy)
53
    {
54
        $this->reader = $reader;
55
        $this->namingStrategy = $namingStrategy;
56
    }
57
58
    public function loadMetadataForClass(\ReflectionClass $class)
59
    {
60
        $metadata = new ClassMetadata($className = $class->getName());
61
        if (false !== $filename = $class->getFilename()) {
62
            $metadata->fileResources[] = $filename;
63
        }
64
65
        // this is a bit of a hack, but avoids any timeout issues when a class
66
        // is moved into one of the compiled classes files, and Doctrine
67
        // Common 2.1 is used.
68
        if (false !== strpos($filename, '/classes.php')
69
            || false !== strpos($filename, '/bootstrap.php')) {
70
            return null;
71
        }
72
73
        foreach ($this->reader->getClassAnnotations($class) as $annot) {
74
            if ($annot instanceof Service) {
75
                if (null === $annot->id) {
76
                    $metadata->id = $this->namingStrategy->classToServiceName($className);
77
                } else {
78
                    $metadata->id = $annot->id;
79
                }
80
81
                $metadata->parent = $annot->parent;
82
                $metadata->public = $annot->public;
83
                $metadata->scope = $annot->scope;
84
                $metadata->abstract = $annot->abstract;
85
                $metadata->decorates = $annot->decorates;
86
                $metadata->decoration_inner_name = $annot->decoration_inner_name;
87
                $metadata->deprecated = $annot->deprecated;
88
            } else if ($annot instanceof Tag) {
89
                $metadata->tags[$annot->name][] = $annot->attributes;
90
            } else if ($annot instanceof Validator) {
91
                // automatically register as service if not done explicitly
92
                if (null === $metadata->id) {
93
                    $metadata->id = $this->namingStrategy->classToServiceName($className);
94
                }
95
96
                $metadata->tags['validator.constraint_validator'][] = array(
97
                    'alias' => $annot->alias,
98
                );
99
            } else if ($annot instanceof AbstractDoctrineListener) {
100
                if (null === $metadata->id) {
101
                    $metadata->id = $this->namingStrategy->classToServiceName($className);
102
                }
103
104
                foreach ($annot->events as $event) {
105
                    $metadata->tags[$annot->getTag()][] = array(
106
                        'event' => $event,
107
                        'connection' => $annot->connection ?: 'default',
108
                        'lazy' => $annot->lazy,
109
                        'priority' => $annot->priority,
110
                    );
111
                }
112
            } else if ($annot instanceof FormType) {
113
                if (null === $metadata->id) {
114
                    $metadata->id = $this->namingStrategy->classToServiceName($className);
115
                }
116
117
                $alias = $annot->alias;
118
119
                // try to extract it from the class itself
120
                if (null === $alias) {
121
                    $instance = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
122
                    $alias = $instance->getName();
123
                }
124
125
                $metadata->tags['form.type'][] = array(
126
                    'alias' => $alias,
127
                );
128 View Code Duplication
            } else if ($annot instanceof MetadataProcessorInterface) {
129
                if (null === $metadata->id) {
130
                    $metadata->id = $this->namingStrategy->classToServiceName($className);
131
                }
132
133
                $annot->processMetadata($metadata);
134
            }
135
        }
136
137
        $hasInjection = false;
138
        foreach ($class->getProperties() as $property) {
139
            if ($property->getDeclaringClass()->getName() !== $className) {
140
                continue;
141
            }
142
            $name = $property->getName();
143
144
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
145
                if ($annot instanceof Inject) {
146
                    $hasInjection = true;
147
                    $metadata->properties[$name] = $this->convertReferenceValue($name, $annot);
148
                }
149
            }
150
        }
151
152
        foreach ($class->getMethods() as $method) {
153
            if ($method->getDeclaringClass()->getName() !== $className) {
154
                continue;
155
            }
156
            $name = $method->getName();
157
158
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
159
                if ($annot instanceof Observe) {
160
                    $metadata->tags['kernel.event_listener'][] = array(
161
                        'event' => $annot->event,
162
                        'method' => $name,
163
                        'priority' => $annot->priority,
164
                    );
165
                } else if ($annot instanceof SecurityFunction) {
166
                    $metadata->tags['security.expressions.function_evaluator'][] = array(
167
                        'function' => $annot->function,
168
                        'method' => $name,
169
                    );
170
                } else if ($annot instanceof InjectParams) {
171
                    $params = array();
172
                    foreach ($method->getParameters() as $param) {
173
                        if (!isset($annot->params[$paramName = $param->getName()])) {
174
                            $params[] = $this->convertReferenceValue($paramName, new Inject(array('value' => null)));
175
                            continue;
176
                        }
177
178
                        $params[] = $this->convertReferenceValue($paramName, $annot->params[$paramName]);
179
                    }
180
181
                    if (!$params) {
182
                        continue;
183
                    }
184
185
                    $hasInjection = true;
186
187
                    if ('__construct' === $name) {
188
                        $metadata->arguments = $params;
189
                    } else {
190
                        $metadata->methodCalls[] = array($name, $params);
191
                    }
192
                } else if ($annot instanceof LookupMethod) {
193
                    $hasInjection = true;
194
195
                    if ($method->isFinal()) {
196
                        throw new \RuntimeException(sprintf('The method "%s::%s" is marked as final and cannot be declared as lookup-method.', $className, $name));
197
                    }
198
                    if ($method->isPrivate()) {
199
                        throw new \RuntimeException(sprintf('The method "%s::%s" is marked as private and cannot be declared as lookup-method.', $className, $name));
200
                    }
201
                    if ($method->getParameters()) {
202
                        throw new \RuntimeException(sprintf('The method "%s::%s" must have a no-arguments signature if you want to use it as lookup-method.', $className, $name));
203
                    }
204
205
                    $metadata->lookupMethods[$name] = $this->convertReferenceValue('get' === substr($name, 0, 3) ? substr($name, 3) : $name, $annot);
206
                } else if ($annot instanceof AfterSetup) {
207
                    if (!$method->isPublic()) {
208
                        throw new \RuntimeException(sprintf('The init method "%s::%s" must be public.', $method->class, $method->name));
209
                    }
210
211
                    $metadata->initMethod = $method->name;
0 ignored issues
show
Deprecated Code introduced by
The property JMS\DiExtraBundle\Metada...ssMetadata::$initMethod has been deprecated with message: since version 1.7, to be removed in 2.0. Use $initMethods instead.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
212
                    $metadata->initMethods[] = $method->name;
213 View Code Duplication
                } else if ($annot instanceof MetadataProcessorInterface) {
214
                    if (null === $metadata->id) {
215
                        $metadata->id = $this->namingStrategy->classToServiceName($className);
216
                    }
217
218
                    $annot->processMetadata($metadata);
219
                }
220
            }
221
        }
222
223
        if (null == $metadata->id && !$hasInjection) {
224
            return null;
225
        }
226
227
        return $metadata;
228
    }
229
230
    private function convertReferenceValue($name, AnnotReference $annot)
231
    {
232 View Code Duplication
        if (null === $annot->value) {
233
            return new Reference($this->namingStrategy->classToServiceName($name), false !== $annot->required ? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE : ContainerInterface::NULL_ON_INVALID_REFERENCE, $annot->strict);
234
        }
235
236 View Code Duplication
        if (false === strpos($annot->value, '%')) {
237
            return new Reference($annot->value, false !== $annot->required ? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE : ContainerInterface::NULL_ON_INVALID_REFERENCE, $annot->strict);
238
        }
239
240
        return $annot->value;
241
    }
242
}
243