Passed
Pull Request — master (#12)
by Thalles
01:39
created

RouterTrait::bindData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 5
dl 0
loc 9
rs 10
1
<?php
2
3
namespace CoffeeCode\Router;
4
5
/**
6
 * Trait CoffeeCode RouterTrait
7
 *
8
 * @author  Robson V. Leite <https://github.com/robsonvleite>
9
 * @package CoffeeCode\Router
10
 */
11
trait RouterTrait
12
{
13
    /** @var array */
14
    protected $routes;
15
    
16
    /** @var string */
17
    protected $patch;
18
    
19
    /** @var string */
20
    protected $httpMethod;
21
    
22
    /**
23
     * @param string     $name
24
     * @param array|null $data
25
     *
26
     * @return string|null
27
     */
28
    public function route(string $name, array $data = null): ?string
29
    {
30
        foreach ($this->routes as $http_verb) {
31
            $routes = [];
32
            
33
            foreach ($http_verb as $route_item) {
34
                if (!empty($route_item["name"]) && $route_item["name"] == $name) {
35
                    $routes[] = $route_item;
36
                }
37
            }
38
            
39
            if (!empty($routes)) {
40
                return $this->treat($routes, $data);
41
            }
42
        }
43
        return null;
44
    }
45
    
46
    /**
47
     * @param string     $route
48
     * @param array|null $data
49
     */
50
    public function redirect(string $route, array $data = null): void
51
    {
52
        if ($name = $this->route($route, $data)) {
53
            header("Location: {$name}");
54
            exit;
55
        }
56
        
57
        if (filter_var($route, FILTER_VALIDATE_URL)) {
58
            header("Location: {$route}");
59
            exit;
60
        }
61
        
62
        $route = (substr($route, 0, 1) == "/" ? $route : "/{$route}");
63
        header("Location: {$this->projectUrl}{$route}");
64
        exit;
65
    }
66
    
67
    /**
68
     * @param string          $method
69
     * @param string          $route
70
     * @param string|callable $handler
71
     * @param null|string
72
     */
73
    protected function addRoute(string $method, string $route, $handler, string $name = null): void
74
    {
75
        if ($route == "/") {
76
            $this->addRoute($method, "", $handler, $name);
77
        }
78
        
79
        preg_match_all("~\{\s* ([a-zA-Z_][a-zA-Z0-9_-]*) \}~x", $route, $keys, PREG_SET_ORDER);
80
        $routeDiff = array_values(array_diff(explode("/", $this->patch), explode("/", $route)));
81
        
82
        $this->formSpoofing();
0 ignored issues
show
Bug introduced by
It seems like formSpoofing() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

82
        $this->/** @scrutinizer ignore-call */ 
83
               formSpoofing();
Loading history...
83
        $offset = ($this->group ? 1 : 0);
84
        foreach ($keys as $key) {
85
            $this->data[$key[1]] = ($routeDiff[$offset++] ?? null);
0 ignored issues
show
Bug Best Practice introduced by
The property data does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
86
        }
87
        
88
        $route = (!$this->group ? $route : "/{$this->group}{$route}");
89
        $data = $this->data;
90
        $namespace = $this->namespace;
91
        $router = function () use ($method, $handler, $data, $route, $name, $namespace) {
92
            return [
93
                "route" => $route,
94
                "name" => $name,
95
                "method" => $method,
96
                "handler" => $this->handler($handler, $namespace),
97
                "action" => $this->action($handler),
98
                "data" => $data
99
            ];
100
        };
101
        
102
        $route = preg_replace('~{([^}]*)}~', "([^/]+)", $route);
103
        $this->routes[$method][$route] = $router();
104
    }
105
    
106
    /**
107
     * @param $handler
108
     * @param $namespace
109
     *
110
     * @return string|callable
111
     */
112
    private function handler($handler, $namespace)
113
    {
114
        return (!is_string($handler) ? $handler : "{$namespace}\\" . explode($this->separator, $handler)[0]);
115
    }
116
    
117
    /**
118
     * @param $handler
119
     *
120
     * @return null|string
121
     */
122
    private function action($handler): ?string
123
    {
124
        return (!is_string($handler) ?: (explode($this->separator, $handler)[1] ?? null));
125
    }
126
    
127
    /**
128
     * @param array      $routes
129
     * @param array|null $data
130
     *
131
     * @return string|null
132
     */
133
    private function treat(array $routes, array $data = null): ?string
134
    {
135
        $route = $routes[0]["route"];
136
        if (!empty($data)) {
137
            $arguments = [];
138
            $params = [];
139
            foreach ($data as $key => $value) {
140
                $new_route = $this->bindData($routes, $arguments, $params, $key, $value);
141
                if ($new_route == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $new_route of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
142
                    $params[$key] = $value;
143
                    continue;
144
                }
145
                $route = $new_route;
146
            }
147
        }
148
        
149
        return "{$this->projectUrl}{$route}";
150
    }
151
    
152
    /**
153
     * @param array  $routes
154
     * @param array  $args
155
     * @param array  $params
156
     * @param string $key
157
     * @param string $value
158
     *
159
     * @return string|null
160
     */
161
    private function bindData(array $routes, array &$args, array &$params, string $key, string $value): ?string
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

161
    private function bindData(array $routes, /** @scrutinizer ignore-unused */ array &$args, array &$params, string $key, string $value): ?string

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

Loading history...
162
    {
163
        foreach ($routes as $route_item) {
164
            if (strstr($route_item["route"], "{{$key}}")) {
165
                $arguments["{{$key}}"] = $value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$arguments was never initialized. Although not strictly required by PHP, it is generally a good practice to add $arguments = array(); before regardless.
Loading history...
166
                return $this->process($route_item["route"], $arguments, $params);
167
            }
168
        }
169
        return null;
170
    }
171
    
172
    /**
173
     * @param string     $route
174
     * @param array      $arguments
175
     * @param array|null $params
176
     *
177
     * @return string
178
     */
179
    private function process(string $route, array $arguments, array $params = null): string
180
    {
181
        $params = (!empty($params) ? "?" . http_build_query($params) : null);
182
        return str_replace(array_keys($arguments), array_values($arguments), $route) . "{$params}";
183
    }
184
}