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) { |
|
|
|
|
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); |
|
|
|
|
197
|
3 |
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Load the available proxies. |
201
|
|
|
*/ |
202
|
6 |
|
protected static function loadProxies() |
203
|
|
|
{ |
204
|
6 |
|
if (static::$proxies !== null) { |
|
|
|
|
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
|
|
|
|
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.