Completed
Push — master ( 984bc1...bcdfa3 )
by Raed
12s queued 10s
created

GeoRoute::orRedirectTo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace LaraCrafts\GeoRoutes;
4
5
use BadMethodCallException;
6
use Illuminate\Routing\Route;
7
use Illuminate\Support\Str;
8
use LaraCrafts\GeoRoutes\Support\Facades\CallbackRegistrar;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, LaraCrafts\GeoRoutes\CallbackRegistrar. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
10
/**
11
 * @mixin \Illuminate\Routing\Route
12
 */
13
class GeoRoute
14
{
15
    /**
16
     * Rule is applied.
17
     *
18
     * @var bool
19
     */
20
    protected $applied;
21
22
    /**
23
     * The callback to execute if the visitor
24
     * is not allowed.
25
     *
26
     * @var array
27
     */
28
    protected $callback;
29
30
    /**
31
     * The countries to apply the rule for.
32
     *
33
     * @var array
34
     */
35
    protected $countries;
36
37
    /**
38
     * The route.
39
     *
40
     * @var \Illuminate\Routing\Route
41
     */
42
    protected $route;
43
44
    /**
45
     * The rule's strategy.
46
     *
47
     * @var string
48
     */
49
    protected $strategy;
50
51
    /**
52
     * Create a new GeoRoute instance.
53
     *
54
     * @param  \Illuminate\Routing\Route $route
55
     * @param  array $countries
56
     * @param  string $strategy
57
     */
58 70
    public function __construct(Route $route, array $countries, string $strategy)
59
    {
60 70
        $this->applied = false;
61 70
        $this->countries = array_map('strtoupper', $countries);
62 70
        $this->route = $route;
63 70
        $this->strategy = $strategy;
64 70
    }
65
66
    /**
67
     * Dynamically call the underlying route.
68
     *
69
     * @param string $method
70
     * @param array $arguments
71
     *
72
     * @return mixed
73
     */
74
    public function __call(string $method, array $arguments)
75
    {
76
        if (method_exists($this->route, $method) || Route::hasMacro($method)) {
77
            return $this->route->$method(...$arguments);
78
        }
79
80
        if (CallbackRegistrar::hasProxy($method)) {
0 ignored issues
show
Bug introduced by
The method hasProxy() does not exist on LaraCrafts\GeoRoutes\Sup...cades\CallbackRegistrar. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

80
        if (CallbackRegistrar::/** @scrutinizer ignore-call */ hasProxy($method)) {
Loading history...
81
            return $this->setCallback(CallbackRegistrar::callback($method), $arguments);
0 ignored issues
show
Bug introduced by
The method callback() does not exist on LaraCrafts\GeoRoutes\Sup...cades\CallbackRegistrar. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

81
            return $this->setCallback(CallbackRegistrar::/** @scrutinizer ignore-call */ callback($method), $arguments);
Loading history...
82
        }
83
84
        throw new BadMethodCallException("Undefined method '$method'");
85
    }
86
87
    /**
88
     * Destruct the GeoRoute instance and apply the middleware.
89
     */
90 70
    public function __destruct()
91
    {
92 70
        $this->applyConstraint();
93 70
    }
94
95
    /**
96
     * Allow given countries.
97
     *
98
     * @return $this
99
     */
100
    public function allow()
101
    {
102
        $this->strategy = 'allow';
103
104
        return $this;
105
    }
106
107
    /**
108
     * Apply the geo-constraint to the route.
109
     */
110 70
    protected function applyConstraint()
111
    {
112 70
        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...
113
            return;
114
        }
115
116 70
        $action = $this->route->getAction();
117 70
        $action['middleware'][] = 'geo';
118 70
        $action['geo'] = [
119 70
            'strategy' => $this->strategy,
120 70
            'countries' => (array)$this->countries,
121 70
            'callback' => $this->callback,
122
        ];
123
124 70
        $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

124
        $this->route->setAction(/** @scrutinizer ignore-type */ $action);
Loading history...
125
126 70
        $this->applied = true;
127 70
    }
128
129
    /**
130
     * Deny given countries.
131
     *
132
     * @return $this
133
     */
134
    public function deny()
135
    {
136
        $this->strategy = 'deny';
137
138
        return $this;
139
    }
140
141
    /**
142
     * Return a HTTP 404 error if access is denied.
143
     *
144
     * @return $this
145
     */
146 14
    public function orNotFound()
147
    {
148 14
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::notFound', func_get_args());
149
    }
150
151
    /**
152
     * Redirect to given route if access is denied.
153
     *
154
     * @param string $routeName
155
     *
156
     * @return $this
157
     */
158 14
    public function orRedirectTo(string $routeName)
159
    {
160 14
        return $this->setCallback('LaraCrafts\GeoRoutes\Callbacks::redirectTo', func_get_args());
161
    }
162
163
    /**
164
     * Return a HTTP 401 error if access is denied (this is the default behavior).
165
     *
166
     * @return $this
167
     */
168
    public function orUnauthorized()
169
    {
170
        $this->callback = null;
171
172
        return $this;
173
    }
174
175
    /**
176
     * Set the callback.
177
     *
178
     * @param callable $callback
179
     * @param array $arguments
180
     *
181
     * @return $this
182
     */
183 28
    protected function setCallback(callable $callback, array $arguments)
184
    {
185 28
        if (is_string($callback) && Str::contains($callback, '@')) {
186
            $callback = Str::parseCallback($callback, '__invoke');
187
            $callback[0] = resolve($callback[0]);
188
        }
189
190 28
        $this->callback = [$callback, $arguments];
191
192 28
        return $this;
193
    }
194
}
195