Passed
Pull Request — master (#21)
by Raed
04:48
created

GeoRoute   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Test Coverage

Coverage 52.31%

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 256
ccs 34
cts 65
cp 0.5231
rs 10
c 0
b 0
f 0
wmc 24

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 11 3
A __destruct() 0 3 1
A __construct() 0 8 1
A __toString() 0 4 2
A allow() 0 5 1
A loadProxies() 0 11 3
A orUnauthorized() 0 5 1
A from() 0 5 1
A setCallback() 0 10 3
A orNotFound() 0 3 1
A orRedirectTo() 0 3 1
A denyFrom() 0 6 1
A allowFrom() 0 6 1
A deny() 0 5 1
A applyMiddleware() 0 11 3
1
<?php
2
3
namespace LaraCrafts\GeoRoutes;
4
5
use BadMethodCallException;
6
use Illuminate\Routing\Route;
7
use Illuminate\Support\Str;
8
9
/**
10
 * @mixin \Illuminate\Routing\Route
11
 */
12
class GeoRoute
13
{
14
    /**
15
     * Rule is applied.
16
     *
17
     * @var bool
18
     */
19
    protected $applied;
20
21
    /**
22
     * The callback to execute if the visitor
23
     * is not allowed.
24
     *
25
     * @var array
26
     */
27
    protected $callback;
28
29
    /**
30
     * The countries to apply the rule for.
31
     *
32
     * @var array
33
     */
34
    protected $countries;
35
36
    /**
37
     * The callbacks' proxies.
38
     *
39
     * @var array
40
     */
41
    protected static $proxies;
42
43
    /**
44
     * The route.
45
     *
46
     * @var \Illuminate\Routing\Route
47
     */
48
    protected $route;
49
50
    /**
51
     * The rule's strategy.
52
     *
53
     * @var string
54
     */
55
    protected $strategy;
56
57
    /**
58
     * Create a new GeoRoute instance.
59
     *
60
     * @param \Illuminate\Routing\Route $route
61
     * @param array $countries
62
     * @param string $strategy
63
     * @throws \InvalidArgumentException
64
     */
65 6
    public function __construct(Route $route, array $countries, string $strategy)
66
    {
67 6
        $this->applied = false;
68 6
        $this->countries = array_map('strtoupper', $countries);
69 6
        $this->route = $route;
70 6
        $this->strategy = $strategy;
71
72 6
        static::loadProxies();
73 6
    }
74
75
    /**
76
     * Dynamically call the underlying route.
77
     *
78
     * @param string $method
79
     * @param array $arguments
80
     *
81
     * @return mixed
82
     */
83
    public function __call(string $method, array $arguments)
84
    {
85
        if (method_exists($this->route, $method)) {
86
            return $this->route->$method(...$arguments);
87
        }
88
89
        if (array_key_exists($method, static::$proxies)) {
90
            return $this->setCallback(static::$proxies[$method], $arguments);
91
        }
92
93
        throw new BadMethodCallException("Undefined method '$method'");
94
    }
95
96
    /**
97
     * Destruct the GeoRoute instance and apply the middleware.
98
     */
99 6
    public function __destruct()
100
    {
101 6
        $this->applyMiddleware();
102 6
    }
103
104
    /**
105
     * Generate a middleware string.
106
     *
107
     * @return string
108
     */
109 3
    public function __toString()
110
    {
111 3
        return 'geo:' . $this->strategy . ',' . implode('&', $this->countries) .
112 3
            ($this->callback ? ',' . serialize($this->callback) : '');
113
    }
114
115
    /**
116
     * Determine the countries covered by the constraint.
117
     *
118
     * @param string ...$countries
119
     *
120
     * @return this
121
     */
122
    public function from(string ...$countries)
123
    {
124
        $this->countries = $countries;
125
126
        return $this;
127
    }
128
129
    /**
130
     * Allow given countries
131
     *
132
     * @param string ...$countries
133
     *
134
     * @return $this
135
     */
136
    public function allowFrom(string ...$countries)
137
    {
138
        $this->strategy = 'allow';
139
        $this->countries = $countries;
140
141
        return $this;
142
    }
143
144
    /**
145
     * Deny given countries
146
     *
147
     * @param string ...$countries
148
     *
149
     * @return $this
150
     */
151
    public function denyFrom(string ...$countries)
152
    {
153
        $this->strategy = 'deny';
154
        $this->countries = $countries;
155
156
        return $this;
157
    }
158
159
    /**
160
     * Allow given countries.
161
     *
162
     * @return $this
163
     */
164 3
    public function allow()
165
    {
166 3
        $this->strategy = 'allow';
167
168 3
        return $this;
169
    }
170
171
    /**
172
     * Deny given countries.
173
     *
174
     * @return $this
175
     */
176 3
    public function deny()
177
    {
178 3
        $this->strategy = 'deny';
179
180 3
        return $this;
181
    }
182
183
    /**
184
     * Apply the middleware to the route.
185
     */
186 6
    protected function applyMiddleware()
187
    {
188 6
        if ($this->applied || !$this->countries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->countries of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
189 3
            return;
190
        }
191
192 3
        $action = $this->route->getAction();
193 3
        $action['middleware'] = (string)$this;
194
195 3
        $this->applied = true;
196 3
        $this->route->setAction($action);
0 ignored issues
show
Bug introduced by
It seems like $action can also be of type null; however, parameter $action of Illuminate\Routing\Route::setAction() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

196
        $this->route->setAction(/** @scrutinizer ignore-type */ $action);
Loading history...
197 3
    }
198
199
    /**
200
     * Load the available proxies.
201
     */
202 6
    protected static function loadProxies()
203
    {
204 6
        if (static::$proxies !== null) {
0 ignored issues
show
introduced by
The condition static::proxies !== null is always true.
Loading history...
205 6
            return;
206
        }
207
208 3
        static::$proxies = [];
209 3
        $callbacks = config('geo-routes.routes.callbacks');
210
211 3
        foreach ($callbacks as $key => $callback) {
212
            static::$proxies['or' . Str::studly($key)] = $callback;
213
        }
214 3
    }
215
216
    /**
217
     * Return a HTTP 404 error if access is denied.
218
     *
219
     * @return $this
220
     */
221
    public function orNotFound()
222
    {
223
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::notFound', func_get_args());
224
    }
225
226
    /**
227
     * Redirect to given route if access is denied.
228
     *
229
     * @param string $routeName
230
     *
231
     * @return $this
232
     */
233
    public function orRedirectTo(string $routeName)
234
    {
235
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::redirectTo', func_get_args());
236
    }
237
238
    /**
239
     * Return a HTTP 401 error if access is denied (this is the default behavior).
240
     *
241
     * @return $this
242
     */
243
    public function orUnauthorized()
244
    {
245
        $this->callback = null;
246
247
        return $this;
248
    }
249
250
    /**
251
     * Set the callback.
252
     *
253
     * @param callable $callback
254
     * @param array $arguments
255
     *
256
     * @return $this
257
     */
258
    protected function setCallback(callable $callback, array $arguments)
259
    {
260
        if (is_string($callback) && Str::contains($callback, '@')) {
261
            $callback = Str::parseCallback($callback, '__invoke');
262
            $callback[0] = resolve($callback[0]);
263
        }
264
265
        $this->callback = [$callback, $arguments];
266
267
        return $this;
268
    }
269
}
270