Issues (5)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/PaginateRoute.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Spatie\PaginateRoute;
4
5
use Illuminate\Routing\Router;
6
use Illuminate\Translation\Translator;
7
use Illuminate\Support\Facades\Request;
8
use Illuminate\Routing\RouteParameterBinder;
9
use Illuminate\Contracts\Pagination\Paginator;
10
use Illuminate\Contracts\Routing\UrlGenerator;
11
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
12
13
class PaginateRoute
14
{
15
    /**
16
     * @var \Illuminate\Translation\Translator
17
     */
18
    protected $translator;
19
20
    /**
21
     * @var \Illuminate\Routing\Router
22
     */
23
    protected $router;
24
25
    /**
26
     * @var \Illuminate\Contracts\Routing\UrlGenerator
27
     */
28
    protected $urlGenerator;
29
30
    /**
31
     * @var string
32
     */
33
    protected $pageKeyword;
34
35
    /**
36
     * @param \Illuminate\Translation\Translator         $translator
37
     * @param \Illuminate\Routing\Router                 $router
38
     * @param \Illuminate\Contracts\Routing\UrlGenerator $urlGenerator
39
     */
40
    public function __construct(Translator $translator, Router $router, UrlGenerator $urlGenerator)
41
    {
42
        $this->translator = $translator;
43
        $this->router = $router;
44
        $this->urlGenerator = $urlGenerator;
45
46
        // Unfortunately we can't do this in the service provider since routes are booted first
47
        $this->translator->addNamespace('paginateroute', __DIR__.'/../resources/lang');
48
49
        $this->pageKeyword = $this->translator->get('paginateroute::paginateroute.page');
50
    }
51
52
    /**
53
     * Return the current page.
54
     *
55
     * @return int
56
     */
57
    public function currentPage()
58
    {
59
        $currentRoute = $this->router->getCurrentRoute();
60
61
        if (! $currentRoute) {
62
            return 1;
63
        }
64
65
        $query = $currentRoute->parameter('pageQuery');
66
67
        return (int) str_replace($this->pageKeyword.'/', '', $query) ?: 1;
68
    }
69
70
    /**
71
     * Check if the given page is the current page.
72
     *
73
     * @param int $page
74
     *
75
     * @return bool
76
     */
77
    public function isCurrentPage($page)
78
    {
79
        return $this->currentPage() === $page;
80
    }
81
82
    /**
83
     * Get the next page number.
84
     *
85
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
86
     *
87
     * @return string|null
88
     */
89
    public function nextPage(Paginator $paginator)
90
    {
91
        if (! $paginator->hasMorePages()) {
92
            return;
93
        }
94
95
        return $this->currentPage() + 1;
96
    }
97
98
    /**
99
     * Determine wether there is a next page.
100
     *
101
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
102
     *
103
     * @return bool
104
     */
105
    public function hasNextPage(Paginator $paginator)
106
    {
107
        return $this->nextPage($paginator) !== null;
108
    }
109
110
    /**
111
     * Get the next page URL.
112
     *
113
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
114
     *
115
     * @return string|null
116
     */
117
    public function nextPageUrl(Paginator $paginator)
118
    {
119
        $nextPage = $this->nextPage($paginator);
120
121
        if ($nextPage === null) {
122
            return;
123
        }
124
125
        return $this->pageUrl($nextPage);
126
    }
127
128
    /**
129
     * Get the previous page number.
130
     *
131
     * @return string|null
132
     */
133
    public function previousPage()
134
    {
135
        if ($this->currentPage() <= 1) {
136
            return;
137
        }
138
139
        return $this->currentPage() - 1;
140
    }
141
142
    /**
143
     * Determine wether there is a previous page.
144
     *
145
     * @return bool
146
     */
147
    public function hasPreviousPage()
148
    {
149
        return $this->previousPage() !== null;
150
    }
151
152
    /**
153
     * Get the previous page URL.
154
     *
155
     * @param bool $full Return the full version of the URL in for the first page
156
     *                   Ex. /users/page/1 instead of /users
157
     *
158
     * @return string|null
159
     */
160
    public function previousPageUrl($full = false)
161
    {
162
        $previousPage = $this->previousPage();
163
164
        if ($previousPage === null) {
165
            return;
166
        }
167
168
        return $this->pageUrl($previousPage, $full);
169
    }
170
171
    /**
172
     * Get all urls in an array.
173
     *
174
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
175
     * @param bool                                                  $full      Return the full version of the URL in for the first page
176
     *                                                                         Ex. /users/page/1 instead of /users
177
     *
178
     * @return array
179
     */
180
    public function allUrls(LengthAwarePaginator $paginator, $full = false)
181
    {
182
        if (! $paginator->hasPages()) {
183
            return [];
184
        }
185
186
        $urls = [];
187
        $left = $this->getLeftPoint($paginator);
188
        $right = $this->getRightPoint($paginator);
189
        for ($page = $left; $page <= $right; $page++) {
190
            $urls[$page] = $this->pageUrl($page, $full);
191
        }
192
193
        return $urls;
194
    }
195
196
    /**
197
     * Get the left most point in the pagination element.
198
     *
199
     * @param LengthAwarePaginator $paginator
200
     * @return int
201
     */
202
    public function getLeftPoint(LengthAwarePaginator $paginator)
203
    {
204
        $side = $paginator->onEachSide;
0 ignored issues
show
Accessing onEachSide on the interface Illuminate\Contracts\Pag...on\LengthAwarePaginator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
205
        $current = $paginator->currentPage();
206
        $last = $paginator->lastPage();
207
208
        if (! empty($side)) {
209
            $x = $current + $side;
210
            $offset = $x >= $last ? $x - $last : 0;
211
            $left = $current - $side - $offset;
212
        }
213
214
        return ! isset($left) || $left < 1 ? 1 : $left;
0 ignored issues
show
The variable $left does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
215
    }
216
217
    /**
218
     * Get the right or last point of the pagination element.
219
     *
220
     * @param LengthAwarePaginator $paginator
221
     * @return int
222
     */
223
    public function getRightPoint(LengthAwarePaginator $paginator)
224
    {
225
        $side = $paginator->onEachSide;
0 ignored issues
show
Accessing onEachSide on the interface Illuminate\Contracts\Pag...on\LengthAwarePaginator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
226
        $current = $paginator->currentPage();
227
        $last = $paginator->lastPage();
228
229
        if (! empty($side)) {
230
            $offset = $current <= $side ? $side - $current + 1 : 0;
231
            $right = $current + $side + $offset;
232
        }
233
234
        return ! isset($right) || $right > $last ? $last : $right;
0 ignored issues
show
The variable $right does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
235
    }
236
237
    /**
238
     * Render a plain html list with previous, next and all urls. The current page gets a current class on the list item.
239
     *
240
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
241
     * @param bool                                                  $full              Return the full version of the URL in for the first page
242
     *                                                                                 Ex. /users/page/1 instead of /users
243
     * @param string                                                $class             Include class on pagination list
244
     *                                                                                 Ex. <ul class="pagination">
245
     * @param bool                                                  $additionalLinks   Include prev and next links on pagination list
246
     *
247
     * @return string
248
     */
249
    public function renderPageList(LengthAwarePaginator $paginator, $full = false, $class = null, $additionalLinks = false)
250
    {
251
        $urls = $this->allUrls($paginator, $full);
252
253
        if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
254
            $class = " class=\"$class\"";
255
        }
256
257
        $listItems = "<ul{$class}>";
258
259
        if ($this->hasPreviousPage() && $additionalLinks) {
260
            $listItems .= "<li><a href=\"{$this->previousPageUrl()}\">&laquo;</a></li>";
261
        }
262
263
        foreach ($urls as $i => $url) {
264
            $pageNum = $i;
265
            $css = '';
266
267
            $link = "<a href=\"{$url}\">{$pageNum}</a>";
268
            if ($pageNum == $this->currentPage()) {
269
                $css = ' class="active"';
270
                $link = $pageNum;
271
            }
272
273
            $listItems .= "<li{$css}>$link</li>";
274
        }
275
276
        if ($this->hasNextPage($paginator) && $additionalLinks) {
277
            $listItems .= "<li><a href=\"{$this->nextPageUrl($paginator)}\">&raquo;</a></li>";
278
        }
279
280
        $listItems .= '</ul>';
281
282
        return $listItems;
283
    }
284
285
    /**
286
     * Render html link tags for SEO indication of previous and next page.
287
     *
288
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
289
     * @param bool                                                  $full       Return the full version of the URL in for the first page
290
     *                                                                          Ex. /users/page/1 instead of /users
291
     *
292
     * @return string
293
     */
294
    public function renderRelLinks(LengthAwarePaginator $paginator, $full = false)
295
    {
296
        $urls = $this->allUrls($paginator, $full);
297
298
        $linkItems = '';
299
300
        foreach ($urls as $i => $url) {
301
            $pageNum = $i + 1;
302
303
            switch ($pageNum - $this->currentPage()) {
304
                case -1:
305
                    $linkItems .= "<link rel=\"prev\" href=\"{$url}\" />";
306
                    break;
307
                case 1:
308
                    $linkItems .= "<link rel=\"next\" href=\"{$url}\" />";
309
                    break;
310
            }
311
        }
312
313
        return $linkItems;
314
    }
315
316
    /**
317
     * @deprecated in favor of renderPageList.
318
     *
319
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
320
     * @param bool                                                  $full      Return the full version of the URL in for the first page
321
     *                                                                         Ex. /users/page/1 instead of /users
322
     *
323
     * @return string
324
     */
325
    public function renderHtml(LengthAwarePaginator $paginator, $full = false)
326
    {
327
        return $this->renderPageList($paginator, $full);
328
    }
329
330
    /**
331
     * Generate a page URL, based on the request's current URL.
332
     *
333
     * @param int  $page
334
     * @param bool $full Return the full version of the URL in for the first page
335
     *                   Ex. /users/page/1 instead of /users
336
     *
337
     * @return string
338
     */
339
    public function pageUrl($page, $full = false)
340
    {
341
        $currentPageUrl = $this->router->getCurrentRoute()->uri();
342
343
        $url = $this->addPageQuery(str_replace('{pageQuery?}', '', $currentPageUrl), $page, $full);
344
345
        foreach ((new RouteParameterBinder($this->router->getCurrentRoute()))->parameters(app('request')) as $parameterName => $parameterValue) {
346
            $url = str_replace(['{'.$parameterName.'}', '{'.$parameterName.'?}'], $parameterValue, $url);
347
        }
348
349
        $query = Request::getQueryString();
350
351
        $query = $query
352
            ? '?'.$query
353
            : '';
354
355
        return $this->urlGenerator->to($url).$query;
356
    }
357
358
    /**
359
     * Append the page query to a URL.
360
     *
361
     * @param string $url
362
     * @param int    $page
363
     * @param bool   $full Return the full version of the URL in for the first page
364
     *                     Ex. /users/page/1 instead of /users
365
     *
366
     * @return string
367
     */
368
    public function addPageQuery($url, $page, $full = false)
369
    {
370
        // If the first page's URL is requested and $full is set to false, there's nothing to be added.
371
        if ($page === 1 && ! $full) {
372
            return $url;
373
        }
374
375
        return trim($url, '/')."/{$this->pageKeyword}/{$page}";
376
    }
377
378
    /**
379
     * Register the Route::paginate macro.
380
     */
381
    public function registerMacros()
382
    {
383
        $pageKeyword = $this->pageKeyword;
384
        $router = $this->router;
385
386
        $router->macro('paginate', function ($uri, $action) use ($pageKeyword, $router) {
387
            $route = null;
388
389
            $router->group(
390
                ['middleware' => 'Spatie\PaginateRoute\SetPageMiddleware'],
391
                function () use ($pageKeyword, $router, $uri, $action, &$route) {
392
                    $route = $router->get($uri.'/{pageQuery?}', $action)->where('pageQuery', $pageKeyword.'/[0-9]+');
393
                });
394
395
            return $route;
396
        });
397
    }
398
}
399