Completed
Pull Request — master (#29)
by Raed
04:25
created

GeoRoute::loadProxies()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 0
dl 0
loc 11
ccs 6
cts 7
cp 0.8571
crap 3.0261
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 60
    public function __construct(Route $route, array $countries, string $strategy)
66
    {
67 60
        $this->applied = false;
68 60
        $this->countries = array_map('strtoupper', $countries);
69 60
        $this->route = $route;
70 60
        $this->strategy = $strategy;
71
72 60
        static::loadProxies();
73 60
    }
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) || Route::hasMacro($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 60
    public function __destruct()
100
    {
101 60
        $this->applyConstraint();
102 60
    }
103
104
    /**
105
     * Allow given countries.
106
     *
107
     * @return $this
108
     */
109
    public function allow()
110
    {
111
        $this->strategy = 'allow';
112
113
        return $this;
114
    }
115
116
    /**
117
     * Apply the geo-constraint to the route.
118
     */
119 60
    protected function applyConstraint()
120
    {
121 60
        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...
122
            return;
123
        }
124
125 60
        $action = $this->route->getAction();
126 60
        $action['middleware'][] = 'geo';
127 60
        $action['geo'] = [
128 60
            'strategy' => $this->strategy,
129 60
            'countries' => (array)$this->countries,
130 60
            'callback' => $this->callback,
131
        ];
132
133 60
        $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

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