Issues (209)

src/Action.php (1 issue)

1
<?php
2
3
namespace Teto\Routing;
4
5
use function array_diff;
6
use function array_fill_keys;
7
use function array_filter;
8
use function array_keys;
9
use function array_values;
10
use function count;
11
use function explode;
12
use function in_array;
13
use function preg_match;
14
use function strlen;
15
use function strpos;
16
use function substr;
17
18
/**
19
 * Action object
20
 *
21
 * @author    USAMI Kenta <[email protected]>
22
 * @copyright 2016 BaguetteHQ
23
 * @license   http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
24
 */
25
class Action
26
{
27
    use \Teto\Object\TypeAssert;
28
    const WILDCARD = '*';
29
30
    /** @var string[] */
31
    public $methods;
32
    /** @var string[] */
33
    public $split_path;
34
    /** @var array */
35
    public $param_pos;
36
    /** @var mixed */
37
    public $value;
38
    /** @var array */
39
    public $param;
40
    /** @var string */
41
    public $extension;
42
    /** @var bool */
43
    public $is_wildcard;
44 52
    /** @var array */
45
    public $available_extensions;
46 52
47
    private static $enum_values = [
48 52
        'methods' => ['GET', 'POST'],
49 52
    ];
50 52
51 52
    /**
52 52
     * @param string[] $methods
53 52
     * @param string[] $split_path
54 52
     * @param array    $param_pos
55 52
     * @param string[] $available_extensions
56 30
     * @param mixed    $value
57 52
     */
58
    public function __construct(array $methods, array $split_path, array $param_pos, array $available_extensions, $value)
59
    {
60
        static::assertMethods($methods);
61
62
        $this->methods     = $methods;
63
        $this->split_path  = $split_path;
64
        $this->param_pos   = $param_pos;
65 59
        $this->value       = $value;
66
        $this->param       = [];
67 59
        $this->is_wildcard = in_array(self::WILDCARD, $available_extensions, true);
68
        $this->available_extensions
69 59
            = empty($available_extensions) ? ['' => true]
70 59
            : array_fill_keys($available_extensions, true) ;
71 7
    }
72
73
    /**
74 54
     * @param  string   $request_method
75 42
     * @param  string[] $request_path
76 7
     * @param  string   $extension
77
     * @return Action|false
78 42
     */
79
    public function match($request_method, array $request_path, $extension)
80
    {
81 54
        $request_len = count($request_path);
82 51
83
        if (!in_array($request_method, $this->methods, true) ||
84 3
            $request_len !== count($this->split_path)) {
85
            return false;
86
        }
87 51
88 15
        if ($this->available_extensions === ['' => true]) {
89
            if (strlen($extension) > 0) {
90
                $request_path[$request_len - 1] .= '.' . $extension;
91 36
            }
92 36
            $extension = '';
93
        }
94 36
95 34
        if ($this->matchExtension($extension)) {
96 20
            $this->extension = $extension;
97 20
        } else {
98
            return false;
99
        }
100 18
101 18
        if (empty($this->param_pos) && ($request_path === $this->split_path)) {
102 18
            return $this;
103 18
        }
104 22
105 6
        foreach ($this->split_path as $i => $p) {
106 24
            $q = $request_path[$i];
107
108
            if (isset($this->param_pos[$i])) {
109
                if (!preg_match($p, $q, $matches)) {
110 16
                    $this->param = [];
111
                    return false;
112
                }
113
114
                $k = $this->param_pos[$i];
115
                $param_tmp = $this->param;
116
                $param_tmp[$k] = isset($matches[1]) ? $matches[1] : $matches[0];
117 54
                $this->param = $param_tmp;
118
            } elseif ($q !== $p) {
119 54
                $this->param = [];
120 50
                return false;
121
            }
122 4
        }
123
124
        return $this;
125
    }
126
127
    /**
128
     * @param  string  $extension
129
     * @return boolean
130
     */
131
    public function matchExtension($extension)
132 16
    {
133
        if (isset($this->available_extensions[$extension])) {
134 16
            return true;
135
        } else {
136 16
            return $this->is_wildcard && $extension !== '';
137 11
        }
138 11
    }
139 11
140
    /**
141 11
     * @param  array   $param
142 4
     * @param  string  $ext
143 4
     * @param  boolean $strict
144
     * @return string
145
     */
146
    public function makePath(array $param, $ext, $strict)
147 12
    {
148 9
        $path = '';
149 7
150 7
        if ($strict) {
151
            $got_keys = array_keys($param);
152
            $expects  = array_values($this->param_pos);
153 6
            $diff     = array_diff($got_keys, $expects);
154
155 6
            if ($diff !== []) {
156 3
                $json = json_encode(array_values($diff));
157
                throw new \DomainException('unnecessary parameters: ' . $json);
158
            }
159 4
        }
160
161
        foreach ($this->split_path as $i => $pattern) {
162 9
            if (!isset($this->param_pos[$i])) {
163 1
                $path .= '/' . $pattern;
164
                continue;
165
            }
166 9
167
            $name = $this->param_pos[$i];
168
169
            if (!isset($param[$name]) || !preg_match($pattern, $param[$name], $matches)) {
170
                throw new \DomainException("Error");
171
            }
172
173
            $path .= '/' . $param[$name];
174
        }
175
176
        if ($ext !== null && $ext !== '') {
177 24
            $path .= '.' . $ext;
178
        }
179 24
180
        return ($path === '') ? '/' : $path;
181 24
    }
182
183 24
    /**
184
     * @param  string   $method_str ex. "GET|POST"
185
     * @param  string   $path       ex. "/dir_name/path"
186
     * @param  mixed    $value
187
     * @param  string[] $ext
188
     * @param  array    $params
189
     * @return Action new instance object
190
     */
191 28
    public static function create($method_str, $path, $value, array $ext, array $params = [])
192
    {
193 28
        $methods = explode('|', $method_str);
194
        list($split_path, $param_pos)
195 28
            = self::parsePathParam($path, $params);
196
197 25
        return new Action($methods, $split_path, $param_pos, $ext, $value);
198 25
    }
199 25
200 25
    /**
201
     * @param  string $path
202 25
     * @param  array  $params
203 25
     * @return array  [$split_path, $param_pos]
204 25
     */
205
    public static function parsePathParam($path, array $params)
206
    {
207 25
        $split_path = array_values(array_filter(explode('/', $path), 'strlen'));
208 25
209
        if (empty($params)) { return [$split_path, []]; }
210 25
211 25
        $new_split_path = [];
212
        $param_pos = [];
213
        foreach ($split_path as $i => $p) {
214
            $variable = null;
215 25
216
            if (strpos($p, ':') !== false) {
217
                $v = substr($p, 1);
218
                if (isset($params[$v])) { $variable = $v; }
219
            }
220
221
            if ($variable === null) {
222
                $new_split_path[] = $p;
223
            } else {
224
                $param_pos[$i]    = $variable;
225
                $new_split_path[] = $params[$v];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $v does not seem to be defined for all execution paths leading up to this point.
Loading history...
226 40
            }
227
        }
228 40
229 40
        return [$new_split_path, $param_pos];
230
    }
231 40
232
    /**
233
     * @param string[] $methods ex. ['GET', 'POST', 'PUT', 'DELETE']
234
     */
235
    public static function setHTTPMethod(array $methods)
236
    {
237
        self::$enum_values['methods'] = $methods;
238
    }
239
240
    protected static function assertMethods(array $methods)
241
    {
242
        foreach ($methods as $m) {
243
            self::assertValue('enum', 'methods', $m, false);
244
        }
245
    }
246
}
247