Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\Routing\Loader;
13
 
14
use Symfony\Component\Config\Loader\FileLoader;
15
use Symfony\Component\Config\Resource\FileResource;
16
use Symfony\Component\Routing\Loader\Configurator\Traits\HostTrait;
17
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
18
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
19
use Symfony\Component\Routing\RouteCollection;
20
use Symfony\Component\Yaml\Exception\ParseException;
21
use Symfony\Component\Yaml\Parser as YamlParser;
22
use Symfony\Component\Yaml\Yaml;
23
 
24
/**
25
 * YamlFileLoader loads Yaml routing files.
26
 *
27
 * @author Fabien Potencier <fabien@symfony.com>
28
 * @author Tobias Schultze <http://tobion.de>
29
 */
30
class YamlFileLoader extends FileLoader
31
{
32
    use HostTrait;
33
    use LocalizedRouteTrait;
34
    use PrefixTrait;
35
 
36
    private static $availableKeys = [
37
        'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude', 'stateless',
38
    ];
39
    private $yamlParser;
40
 
41
    /**
42
     * Loads a Yaml file.
43
     *
44
     * @param string      $file A Yaml file path
45
     * @param string|null $type The resource type
46
     *
47
     * @return RouteCollection A RouteCollection instance
48
     *
49
     * @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid
50
     */
51
    public function load($file, string $type = null)
52
    {
53
        $path = $this->locator->locate($file);
54
 
55
        if (!stream_is_local($path)) {
56
            throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path));
57
        }
58
 
59
        if (!file_exists($path)) {
60
            throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path));
61
        }
62
 
63
        if (null === $this->yamlParser) {
64
            $this->yamlParser = new YamlParser();
65
        }
66
 
67
        try {
68
            $parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
69
        } catch (ParseException $e) {
70
            throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e);
71
        }
72
 
73
        $collection = new RouteCollection();
74
        $collection->addResource(new FileResource($path));
75
 
76
        // empty file
77
        if (null === $parsedConfig) {
78
            return $collection;
79
        }
80
 
81
        // not an array
82
        if (!\is_array($parsedConfig)) {
83
            throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
84
        }
85
 
86
        foreach ($parsedConfig as $name => $config) {
87
            $this->validate($config, $name, $path);
88
 
89
            if (isset($config['resource'])) {
90
                $this->parseImport($collection, $config, $path, $file);
91
            } else {
92
                $this->parseRoute($collection, $name, $config, $path);
93
            }
94
        }
95
 
96
        return $collection;
97
    }
98
 
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function supports($resource, string $type = null)
103
    {
104
        return \is_string($resource) && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yml', 'yaml'], true) && (!$type || 'yaml' === $type);
105
    }
106
 
107
    /**
108
     * Parses a route and adds it to the RouteCollection.
109
     *
110
     * @param string $name   Route name
111
     * @param array  $config Route definition
112
     * @param string $path   Full path of the YAML file being processed
113
     */
114
    protected function parseRoute(RouteCollection $collection, string $name, array $config, string $path)
115
    {
116
        $defaults = isset($config['defaults']) ? $config['defaults'] : [];
117
        $requirements = isset($config['requirements']) ? $config['requirements'] : [];
118
        $options = isset($config['options']) ? $config['options'] : [];
119
 
120
        foreach ($requirements as $placeholder => $requirement) {
121
            if (\is_int($placeholder)) {
122
                throw new \InvalidArgumentException(sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" of route "%s" in "%s"?', $placeholder, $requirement, $name, $path));
123
            }
124
        }
125
 
126
        if (isset($config['controller'])) {
127
            $defaults['_controller'] = $config['controller'];
128
        }
129
        if (isset($config['locale'])) {
130
            $defaults['_locale'] = $config['locale'];
131
        }
132
        if (isset($config['format'])) {
133
            $defaults['_format'] = $config['format'];
134
        }
135
        if (isset($config['utf8'])) {
136
            $options['utf8'] = $config['utf8'];
137
        }
138
        if (isset($config['stateless'])) {
139
            $defaults['_stateless'] = $config['stateless'];
140
        }
141
 
142
        $routes = $this->createLocalizedRoute($collection, $name, $config['path']);
143
        $routes->addDefaults($defaults);
144
        $routes->addRequirements($requirements);
145
        $routes->addOptions($options);
146
        $routes->setSchemes($config['schemes'] ?? []);
147
        $routes->setMethods($config['methods'] ?? []);
148
        $routes->setCondition($config['condition'] ?? null);
149
 
150
        if (isset($config['host'])) {
151
            $this->addHost($routes, $config['host']);
152
        }
153
    }
154
 
155
    /**
156
     * Parses an import and adds the routes in the resource to the RouteCollection.
157
     *
158
     * @param array  $config Route definition
159
     * @param string $path   Full path of the YAML file being processed
160
     * @param string $file   Loaded file name
161
     */
162
    protected function parseImport(RouteCollection $collection, array $config, string $path, string $file)
163
    {
164
        $type = isset($config['type']) ? $config['type'] : null;
165
        $prefix = isset($config['prefix']) ? $config['prefix'] : '';
166
        $defaults = isset($config['defaults']) ? $config['defaults'] : [];
167
        $requirements = isset($config['requirements']) ? $config['requirements'] : [];
168
        $options = isset($config['options']) ? $config['options'] : [];
169
        $host = isset($config['host']) ? $config['host'] : null;
170
        $condition = isset($config['condition']) ? $config['condition'] : null;
171
        $schemes = isset($config['schemes']) ? $config['schemes'] : null;
172
        $methods = isset($config['methods']) ? $config['methods'] : null;
173
        $trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
174
        $namePrefix = $config['name_prefix'] ?? null;
175
        $exclude = $config['exclude'] ?? null;
176
 
177
        if (isset($config['controller'])) {
178
            $defaults['_controller'] = $config['controller'];
179
        }
180
        if (isset($config['locale'])) {
181
            $defaults['_locale'] = $config['locale'];
182
        }
183
        if (isset($config['format'])) {
184
            $defaults['_format'] = $config['format'];
185
        }
186
        if (isset($config['utf8'])) {
187
            $options['utf8'] = $config['utf8'];
188
        }
189
        if (isset($config['stateless'])) {
190
            $defaults['_stateless'] = $config['stateless'];
191
        }
192
 
193
        $this->setCurrentDir(\dirname($path));
194
 
195
        /** @var RouteCollection[] $imported */
196
        $imported = $this->import($config['resource'], $type, false, $file, $exclude) ?: [];
197
 
198
        if (!\is_array($imported)) {
199
            $imported = [$imported];
200
        }
201
 
202
        foreach ($imported as $subCollection) {
203
            $this->addPrefix($subCollection, $prefix, $trailingSlashOnRoot);
204
 
205
            if (null !== $host) {
206
                $this->addHost($subCollection, $host);
207
            }
208
            if (null !== $condition) {
209
                $subCollection->setCondition($condition);
210
            }
211
            if (null !== $schemes) {
212
                $subCollection->setSchemes($schemes);
213
            }
214
            if (null !== $methods) {
215
                $subCollection->setMethods($methods);
216
            }
217
            if (null !== $namePrefix) {
218
                $subCollection->addNamePrefix($namePrefix);
219
            }
220
            $subCollection->addDefaults($defaults);
221
            $subCollection->addRequirements($requirements);
222
            $subCollection->addOptions($options);
223
 
224
            $collection->addCollection($subCollection);
225
        }
226
    }
227
 
228
    /**
229
     * Validates the route configuration.
230
     *
231
     * @param array  $config A resource config
232
     * @param string $name   The config key
233
     * @param string $path   The loaded file path
234
     *
235
     * @throws \InvalidArgumentException If one of the provided config keys is not supported,
236
     *                                   something is missing or the combination is nonsense
237
     */
238
    protected function validate($config, string $name, string $path)
239
    {
240
        if (!\is_array($config)) {
241
            throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
242
        }
243
        if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) {
244
            throw new \InvalidArgumentException(sprintf('The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".', $path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)));
245
        }
246
        if (isset($config['resource']) && isset($config['path'])) {
247
            throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.', $path, $name));
248
        }
249
        if (!isset($config['resource']) && isset($config['type'])) {
250
            throw new \InvalidArgumentException(sprintf('The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.', $name, $path));
251
        }
252
        if (!isset($config['resource']) && !isset($config['path'])) {
253
            throw new \InvalidArgumentException(sprintf('You must define a "path" for the route "%s" in file "%s".', $name, $path));
254
        }
255
        if (isset($config['controller']) && isset($config['defaults']['_controller'])) {
256
            throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" key and the defaults key "_controller" for "%s".', $path, $name));
257
        }
258
        if (isset($config['stateless']) && isset($config['defaults']['_stateless'])) {
259
            throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "stateless" key and the defaults key "_stateless" for "%s".', $path, $name));
260
        }
261
    }
262
}