Completed
Pull Request — master (#5)
by Pavel
04:05
created

YamlLoader::parseRoute()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 15
ccs 12
cts 12
cp 1
rs 8.8571
cc 5
eloc 11
nc 8
nop 4
crap 5
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: batanov.pavel
5
 * Date: 12.02.2016
6
 * Time: 15:21
7
 */
8
9
namespace Bankiru\Api\Rpc\Routing\Loader;
10
11
use Bankiru\Api\Rpc\Routing\MethodCollection;
12
use Bankiru\Api\Rpc\Routing\Route;
13
use Symfony\Component\Config\Resource\FileResource;
14
use Symfony\Component\Yaml\Exception\ParseException;
15
use Symfony\Component\Yaml\Parser;
16
17
class YamlLoader extends FileLoader
18
{
19
    private static $availableKeys = [
20
        'resource',
21
        'type',
22
        'prefix',
23
        'method',
24
        'controller',
25
        'context',
26
        'default_context',
27
        'inherit',
28
    ];
29
30
    /** @var  Parser */
31
    private $parser;
32
33
    /**
34
     * Loads a resource.
35
     *
36
     * @param mixed       $resource The resource
37
     * @param string|null $type     The resource type or null if unknown
38
     *
39
     * @return MethodCollection
40
     * @throws \Exception If something went wrong
41
     */
42 8
    public function load($resource, $type = null)
43
    {
44 8
        $path = $this->getLocator()->locate($resource);
45
46 8
        if (!stream_is_local($path)) {
47
            throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path));
48
        }
49
50 8
        if (!file_exists($path)) {
51
            throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path));
52
        }
53
54 8
        if (null === $this->parser) {
55 8
            $this->parser = new Parser();
56 8
        }
57
58
        try {
59 8
            $parsedConfig = $this->parser->parse(file_get_contents($path));
60 8
        } catch (ParseException $e) {
61
            throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
62
        }
63
64 8
        $collection = new MethodCollection();
65 8
        $collection->addResource(new FileResource($path));
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->getLocator()->locate($resource) on line 44 can also be of type array; however, Symfony\Component\Config...Resource::__construct() does only seem to accept string, 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...
66
67
        // empty file
68 8
        if (null === $parsedConfig) {
69
            return $collection;
70
        }
71
72
        // not an array
73 8
        if (!is_array($parsedConfig)) {
74
            throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
75
        }
76
77 8
        foreach ($parsedConfig as $name => $config) {
78 8
            $this->validate($config, $name, $path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->getLocator()->locate($resource) on line 44 can also be of type array; however, Bankiru\Api\Rpc\Routing\...\YamlLoader::validate() does only seem to accept string, 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...
79
80 8
            if (isset($config['resource'])) {
81 8
                $this->parseImport($collection, $config, $path, $resource);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->getLocator()->locate($resource) on line 44 can also be of type array; however, Bankiru\Api\Rpc\Routing\...mlLoader::parseImport() does only seem to accept string, 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...
82 8
            } else {
83 8
                $this->parseRoute($collection, $name, $config, $path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->getLocator()->locate($resource) on line 44 can also be of type array; however, Bankiru\Api\Rpc\Routing\...amlLoader::parseRoute() does only seem to accept string, 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...
84
            }
85 8
        }
86
87 8
        return $collection;
88
    }
89
90
    /**
91
     * Validates the route configuration.
92
     *
93
     * @param array  $config A resource config
94
     * @param string $name   The config key
95
     * @param string $path   The loaded file path
96
     *
97
     * @return array
98
     * @throws \InvalidArgumentException If one of the provided config keys is not supported,
99
     *                                   something is missing or the combination is nonsense
100
     */
101 8
    protected function validate($config, $name, $path)
102
    {
103 8
        if (!is_array($config)) {
104
            throw new \InvalidArgumentException(
105
                sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path)
106
            );
107
        }
108 8
        if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) {
109
            throw new \InvalidArgumentException(
110
                sprintf(
111
                    'The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".',
112
                    $path,
113
                    $name,
114
                    implode('", "', $extraKeys),
115
                    implode('", "', self::$availableKeys)
116
                )
117
            );
118
        }
119 8 View Code Duplication
        if (isset($config['resource']) && isset($config['method'])) {
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...
120
            throw new \InvalidArgumentException(
121
                sprintf(
122
                    'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". ' .
123
                    'Choose between an import and a route definition.',
124
                    $path,
125
                    $name
126
                )
127
            );
128
        }
129 8 View Code Duplication
        if (!isset($config['resource']) && isset($config['type'])) {
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...
130
            throw new \InvalidArgumentException(
131
                sprintf(
132
                    'The "type" key for the route definition "%s" in "%s" is unsupported. ' .
133
                    'It is only available for imports in combination with the "resource" key.',
134
                    $name,
135
                    $path
136
                )
137
            );
138
        }
139 8
    }
140
141
    /**
142
     * Parses an import and adds the routes in the resource to the RouteCollection.
143
     *
144
     * @param MethodCollection $collection A RouteCollection instance
145
     * @param array            $config     Route definition
146
     * @param string           $path       Full path of the YAML file being processed
147
     * @param string           $file       Loaded file name
148
     */
149 8
    protected function parseImport(MethodCollection $collection, array $config, $path, $file)
150
    {
151 8
        $type   = isset($config['type']) ? $config['type'] : null;
152 8
        $prefix = isset($config['prefix']) ? $config['prefix'] : '';
153 8
        $this->setCurrentDir(dirname($path));
154
155 8
        $subCollection = $this->import($config['resource'], $type, false, $file);
156
        /* @var $subCollection MethodCollection */
157 8
        $subCollection->addPrefix($prefix);
158
159 8
        if (array_key_exists('context', $config)) {
160 8
            foreach ((array)$config['context'] as $context) {
161 8
                $subCollection->addContext($context);
162 8
            }
163 8
        }
164
165 8
        $collection->addCollection($subCollection);
166 8
    }
167
168
    /**
169
     * Parses a route and adds it to the RouteCollection.
170
     *
171
     * @param MethodCollection $collection A RouteCollection instance
172
     * @param string           $name       Route name
173
     * @param array            $config     Route definition
174
     * @param string           $path       Full path of the YAML file being processed
175
     */
176 8
    protected function parseRoute(MethodCollection $collection, $name, array $config, $path)
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
177
    {
178 8
        $context = array_key_exists('context', $config) ? (array)$config['context'] : [];
179 8
        $method  = array_key_exists('method', $config) ? $config['method'] : $name;
180 8
        $inherit = array_key_exists('inherit', $config) ? $config['inherit'] : true;
181 8
        $route   = new Route(
182 8
            $method,
183 8
            $config['controller'],
184 8
            $context,
185 8
            array_key_exists('default_context', $config) ? $config['default_context'] : true,
186
            $inherit
187 8
        );
188
189 8
        $collection->add($name, $route);
190 8
    }
191
192
    /**
193
     * Returns whether this class supports the given resource.
194
     *
195
     * @param mixed       $resource A resource
196
     * @param string|null $type     The resource type or null if unknown
197
     *
198
     * @return bool True if this class supports the given resource, false otherwise
199
     */
200 8
    public function supports($resource, $type = null)
201
    {
202 8
        return is_string($resource) &&
203 8
               in_array(pathinfo($resource, PATHINFO_EXTENSION), ['yml', 'yaml'], true) &&
204 8
               (!$type || 'yaml' === $type);
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
205
    }
206
}
207