Passed
Pull Request — master (#9)
by Pavel
05:20
created

YamlLoader::supports()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

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