View::removeControllerFromPrefixes()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace JumpGate\ViewResolution\Models;
4
5
use Illuminate\Support\Collection;
6
use ReflectionClass;
7
8
class View
9
{
10
    /**
11
     * @var null|string
12
     */
13
    public $layout = null;
14
    /**
15
     * @var null|string
16
     */
17
    public $fullController = null;
18
19
    /**
20
     * @var null|string
21
     */
22
    public $configIndex = null;
23
24
    /**
25
     * @var null|string
26
     */
27
    public $prefix = null;
28
29
    /**
30
     * @var null|string
31
     */
32
    public $controller = null;
33
34
    /**
35
     * @var null|string
36
     */
37
    public $action = null;
38
39
    /**
40
     * @var null|string
41
     */
42
    public $view = null;
43
44
    /**
45
     * @var string
46
     */
47
    public $type = 'manual';
48
49
    /**
50
     * @var Collection
51
     */
52
    public $prefixes;
53
54
    /**
55
     * @var Collection
56
     */
57
    public $attemptedViews;
58
59
    /**
60
     * @param array $routeParts
61
     * @param null  $layout
62
     */
63
    public function __construct(array $routeParts = [], $layout = null)
64
    {
65
        $this->attemptedViews = collect();
66
        $this->layout         = $layout;
67
68
        if (! empty($routeParts)) {
69
            $this->parseController(head($routeParts));
70
            $this->parseAction(last($routeParts));
71
            $this->getPrefixes();
72
            $this->setView();
73
        }
74
    }
75
76
    /**
77
     * Find the most reasonable view available.
78
     *
79
     * @return null
80
     */
81
    public function getView()
82
    {
83
        // If we don't have a prefix, just return the view.
84
        if (is_null($this->prefix)) {
85
            return $this->getBaseView();
86
        }
87
88
        // Set up modifiable variables.
89
        $view     = $this->concatViewAndPrefix($this->prefix, $this->view);
90
        $prefixes = clone $this->prefixes;
91
92
        $this->attempted($view);
93
94
        // Try to find a valid view.
95
        while (! view()->exists($view)) {
0 ignored issues
show
Bug introduced by
The method exists does only exist in Illuminate\Contracts\View\Factory, but not in Illuminate\View\View.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
96
            // If we are out of prefixes and the view still isn't found, back out.
97
            if (is_null($this->prefix) && ! view()->exists($view)) {
98
                $view = null;
99
                break;
100
            }
101
102
            // Remove prefixes until we don't have any left.
103
            if ($prefixes->count() > 0) {
104
                $prefixes->pop();
105
106
                $prefixes     = $this->removeControllerFromPrefixes($prefixes);
107
                $this->prefix = $prefixes->count() > 0 ? $prefixes->implode('.') : null;
108
                $view         = $this->concatViewAndPrefix($this->prefix, $this->view);
109
            } else {
110
                $this->prefix = null;
111
                $view         = $this->view;
112
            }
113
114
            $this->attempted($view);
115
        }
116
117
        $this->view = $view;
118
119
        return $this->view;
120
    }
121
122
    /**
123
     * When a view is checked, add it to attempted.
124
     *
125
     * @param string $view
126
     */
127
    protected function attempted($view)
128
    {
129
        $this->attemptedViews = $this->attemptedViews->push($view);
130
    }
131
132
    /**
133
     * Combine a prefix and view to get a full path.
134
     *
135
     * @param string $prefix
136
     * @param string $view
137
     *
138
     * @return string
139
     */
140
    public function concatViewAndPrefix($prefix, $view)
141
    {
142
        if (is_null($prefix) || ! $prefix) {
143
            return $view;
144
        }
145
146
        return $prefix . '.' . $view;
147
    }
148
149
    /**
150
     * Get a properly formatted controller name.
151
     *
152
     * @param string $class
153
     *
154
     * @return string
155
     */
156
    protected function parseController($class)
157
    {
158
        $controller           = new ReflectionClass($class);
159
        $this->fullController = $controller->name;
160
        $class                = $controller->getShortName();
161
        $this->controller     = strtolower(str_replace('Controller', '', $class));
162
    }
163
164
    /**
165
     * Get a properly formatted action name.
166
     *
167
     * @param string $action
168
     *
169
     * @return string
170
     */
171
    protected function parseAction($action)
172
    {
173
        if ($action === $this->fullController) {
174
            return $this->action = null;
175
        }
176
177
        $this->action = strtolower(
178
            preg_replace(['/^get/', '/^post/', '/^put/', '/^patch/', '/^delete/'], '', $action)
179
        );
180
    }
181
182
    /**
183
     * Search for any prefixes attached to this route.
184
     *
185
     * @return string
186
     */
187
    protected function getPrefixes()
188
    {
189
        $router = app(\Illuminate\Routing\Router::class);
190
191
        $this->prefixes = collect(
192
            explode('/', $router->getCurrentRoute()->getPrefix())
193
        );
194
195
        // Remove the last prefix if it matches the controller.
196
        $this->prefixes = $this->removeControllerFromPrefixes($this->prefixes)->filter();
197
198
        if ($this->prefixes->count() > 0) {
199
            $this->prefix = $this->prefixes->filter()->implode('.');
200
        }
201
    }
202
203
    /**
204
     * Remove the last prefix if it matches the controller.
205
     *
206
     * @param \Illuminate\Support\Collection $prefixes
207
     *
208
     * @return mixed
209
     */
210
    protected function removeControllerFromPrefixes($prefixes)
211
    {
212
        if ($prefixes->last() == $this->controller) {
213
            $prefixes->pop();
214
        }
215
216
        return $prefixes;
217
    }
218
219
    /**
220
     * Combine the controller and action to create a proper view string.
221
     */
222
    protected function setView()
223
    {
224
        $views = [
225
            $this->controller,
226
            $this->action,
227
        ];
228
229
        $this->view = implode('.', array_filter($views));
230
    }
231
232
    /**
233
     * Return the base view if it exists.
234
     *
235
     * @return null|string
236
     */
237
    private function getBaseView()
238
    {
239
        $this->attempted($this->view);
240
241
        return view()->exists($this->view) ? $this->view : null;
0 ignored issues
show
Bug introduced by
The method exists does only exist in Illuminate\Contracts\View\Factory, but not in Illuminate\View\View.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
242
    }
243
244
    /**
245
     * Check the view routing config for the controller and method.
246
     *
247
     * @return null|string
248
     */
249
    public function checkConfig()
250
    {
251
        $views = [
252
            $this->fullController,
253
            $this->action,
254
        ];
255
256
        $this->configIndex = implode('.', array_filter($views));
257
258
        return array_get(config('jumpgate.view-resolution.view_locations'), $this->configIndex);
259
    }
260
}
261