Completed
Pull Request — master (#17)
by Raed
15:29
created

GeoRoute::setCallback()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 2
dl 0
loc 10
ccs 4
cts 6
cp 0.6667
crap 3.3332
rs 10
c 0
b 0
f 0
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 142
    public function __construct(Route $route, array $countries, string $strategy)
66
    {
67 142
        $this->applied = false;
68 142
        $this->countries = array_map('strtoupper', $countries);
69 142
        $this->route = $route;
70 142
        $this->strategy = $strategy;
71
72 142
        static::loadProxies();
73 142
    }
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 142
    public function __destruct()
100
    {
101 142
        $this->applyMiddleware();
102 142
    }
103
104
    /**
105
     * Generate a middleware string.
106
     *
107
     * @return string
108
     */
109 115
    public function __toString()
110
    {
111 115
        return 'geo:' . $this->strategy . ',' . implode('&', $this->countries) .
112 115
            ($this->callback ? ',' . serialize($this->callback) : '');
113
    }
114
115
    /**
116
     * Allow given countries.
117
     *
118
     * @return $this
119
     */
120 27
    public function allow()
121
    {
122 27
        $this->strategy = 'allow';
123
124 27
        return $this;
125
    }
126
127
    /**
128
     * Apply the middleware to the route.
129
     */
130 142
    protected function applyMiddleware()
131
    {
132 142
        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...
133 27
            return;
134
        }
135
136 115
        $action = $this->route->getAction();
137 115
        $action['middleware'][] = (string)$this;
138
139 115
        $this->applied = true;
140 115
        $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

140
        $this->route->setAction(/** @scrutinizer ignore-type */ $action);
Loading history...
141 115
    }
142
143
    /**
144
     * Deny given countries.
145
     *
146
     * @return $this
147
     */
148 27
    public function deny()
149
    {
150 27
        $this->strategy = 'deny';
151
152 27
        return $this;
153
    }
154
155
    /**
156
     * Load the available proxies.
157
     */
158 142
    protected static function loadProxies()
159
    {
160 142
        if (static::$proxies !== null) {
0 ignored issues
show
introduced by
The condition static::proxies !== null is always true.
Loading history...
161 142
            return;
162
        }
163
164 27
        static::$proxies = [];
165 27
        $callbacks = config('geo-routes.callbacks');
166
167 27
        foreach ($callbacks as $key => $callback) {
168
            static::$proxies['or' . Str::studly($key)] = $callback;
169
        }
170 27
    }
171
172
    /**
173
     * Return a HTTP 404 error if access is denied.
174
     *
175
     * @return $this
176
     */
177 26
    public function orNotFound()
178
    {
179 26
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::notFound', func_get_args());
180
    }
181
182
    /**
183
     * Redirect to given route if access is denied.
184
     *
185
     * @param string $routeName
186
     *
187
     * @return $this
188
     */
189 26
    public function orRedirectTo(string $routeName)
190
    {
191 26
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::redirectTo', func_get_args());
192
    }
193
194
    /**
195
     * Return a HTTP 401 error if access is denied (this is the default behavior).
196
     *
197
     * @return $this
198
     */
199
    public function orUnauthorized()
200
    {
201
        $this->callback = null;
202
203
        return $this;
204
    }
205
206
    /**
207
     * Set the callback.
208
     *
209
     * @param callable $callback
210
     * @param array $arguments
211
     *
212
     * @return $this
213
     */
214 52
    protected function setCallback(callable $callback, array $arguments)
215
    {
216 52
        if (is_string($callback) && Str::contains($callback, '@')) {
217
            $callback = Str::parseCallback($callback, '__invoke');
218
            $callback[0] = resolve($callback[0]);
219
        }
220
221 52
        $this->callback = [$callback, $arguments];
222
223 52
        return $this;
224
    }
225
}
226