Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — match-route-file-end (#5580)
by Pedro
35:51 queued 20:57
created

getLastLineNumberThatContains()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Backpack\CRUD\app\Console\Commands;
4
5
use Illuminate\Console\Command;
6
7
class AddCustomRouteContent extends Command
8
{
9
    use \Backpack\CRUD\app\Console\Commands\Traits\PrettyCommandOutput;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Consol...its\PrettyCommandOutput requires some properties which are not provided by Backpack\CRUD\app\Consol...s\AddCustomRouteContent: $progressBar, $statusColor, $status
Loading history...
10
11
    private $backpackCustomRouteFile = 'routes/backpack/custom.php';
12
13
    /**
14
     * The name and signature of the console command.
15
     *
16
     * @var string
17
     */
18
    protected $signature = 'backpack:add-custom-route
19
                                {code : HTML/PHP code that registers a route. Use either single quotes or double quotes. Never both. }
20
                                {--route-file=routes/backpack/custom.php : The file where the code should be added relative to the root of the project. }';
21
22
    /**
23
     * The console command description.
24
     *
25
     * @var string
26
     */
27
    protected $description = 'Add HTML/PHP code to the routes/backpack/custom.php file';
28
29
    /**
30
     * Create a new command instance.
31
     *
32
     * @return void
33
     */
34
    public function __construct()
35
    {
36
        parent::__construct();
37
    }
38
39
    /**
40
     * Execute the console command.
41
     *
42
     * @return mixed
43
     */
44
    public function handle()
45
    {
46
        $routeFilePath = base_path($this->option('route-file'));
47
48
        if (! file_exists($routeFilePath)) {
49
            if ($routeFilePath !== base_path($this->backpackCustomRouteFile)) {
50
                $this->info('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Please create it first.');
51
52
                return 1;
53
            }
54
55
            $createRouteFile = $this->confirm('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Should we create it?', 'yes');
0 ignored issues
show
Bug introduced by
'yes' of type string is incompatible with the type boolean expected by parameter $default of Illuminate\Console\Command::confirm(). ( Ignorable by Annotation )

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

55
            $createRouteFile = $this->confirm('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Should we create it?', /** @scrutinizer ignore-type */ 'yes');
Loading history...
56
            if ($createRouteFile === 'yes') {
0 ignored issues
show
introduced by
The condition $createRouteFile === 'yes' is always false.
Loading history...
57
                $this->call('vendor:publish', ['--provider' => \Backpack\CRUD\BackpackServiceProvider::class, '--tag' => 'custom_routes']);
58
            } else {
59
                $this->info('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Please create it first.');
60
61
                return 1;
62
            }
63
        }
64
65
        $code = $this->argument('code');
66
67
        $this->progressBlock("Adding route to <fg=blue>$routeFilePath</>");
68
69
        $originalContent = file($routeFilePath);
70
71
        // clean the content from comments etc
72
        $cleanContent = $this->cleanContentArray($originalContent);
73
        $cleanCode = $this->cleanCodeString($code);
74
75
        // if the content contains code, don't add it again.
76
        if (array_search($cleanCode, $cleanContent, true) !== false) {
77
            $this->closeProgressBlock('Already existed', 'yellow');
78
79
            return;
80
        }
81
82
        // get the last element of the array contains '}'
83
        $lastLine = $this->getLastLineNumberThatContains('}', $cleanContent);
84
85
        if ($lastLine === false) {
0 ignored issues
show
introduced by
The condition $lastLine === false is always false.
Loading history...
86
            $this->closeProgressBlock('Could not find the last line, file '.$routeFilePath.' may be corrupted.', 'red');
87
88
            return;
89
        }
90
91
        // add the code to the line before the last line
92
        array_splice($originalContent, $lastLine, 0, '    '.$code.PHP_EOL);
93
94
        // write the new content to the file
95
        if (file_put_contents($routeFilePath, implode('', $originalContent)) === false) {
96
            $this->closeProgressBlock('Failed to add route. Failed writing the modified route file. Maybe check file permissions?', 'red');
97
98
            return;
99
        }
100
101
        $this->closeProgressBlock('done', 'green');
102
    }
103
104
    private function cleanCodeString($code)
105
    {
106
        $code = trim($code);
107
        $code = str_replace('"', "'", $code);
108
        $code = preg_replace('/function\(.*\)/', '', $code);
109
        $code = preg_replace('/fn\(.*\)/', '', $code);
110
        $code = preg_replace('/\[.*\]/', '', $code);
111
112
        return $code;
113
    }
114
115
    private function cleanContentArray(array $content)
116
    {
117
        return array_filter(array_map(function ($line) {
118
            $lineText = trim($line);
119
            if ($lineText === '' ||
120
                $lineText === '\n' ||
121
                $lineText === '\r' ||
122
                $lineText === '\r\n' ||
123
                $lineText === PHP_EOL ||
124
                str_starts_with($lineText, '<?php') ||
125
                str_starts_with($lineText, '?>') ||
126
                str_starts_with($lineText, '//') ||
127
                str_starts_with($lineText, '/*') ||
128
                str_starts_with($lineText, '*/') ||
129
                str_ends_with($lineText, '*/') ||
130
                str_starts_with($lineText, '*') ||
131
                str_starts_with($lineText, 'use ') ||
132
                str_starts_with($lineText, 'return ') ||
133
                str_starts_with($lineText, 'namespace ')) {
134
                return null;
135
            }
136
137
            // replace all double quotes with single quotes for easier comparison
138
            $lineText = str_replace('"', "'", $lineText);
139
140
            // if line starts with Route:: remove anything as function() { ... } and fn () => ...;
141
            if (str_starts_with($lineText, 'Route::')) {
142
                $lineText = preg_replace('/function\(.*\)/', '', $lineText);
143
                $lineText = preg_replace('/fn\(.*\)/', '', $lineText);
144
            }
145
146
            // remove everything inside []
147
            $lineText = preg_replace('/\[.*\]/', '', $lineText);
148
149
            return $lineText;
150
        }, $content));
151
    }
152
153
    /**
154
     * Parse the given file stream and return the line number where a string is found.
155
     *
156
     * @param  string  $needle  The string that's being searched for.
157
     * @param  array  $haystack  The file where the search is being performed.
158
     * @return bool|int The last line number where the string was found. Or false.
159
     */
160
    private function getLastLineNumberThatContains($needle, $haystack)
161
    {
162
        $matchingLines = array_filter($haystack, function ($k) use ($needle) {
163
            return strpos($k, $needle) !== false;
164
        });
165
166
        if ($matchingLines) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matchingLines 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...
167
            return array_key_last($matchingLines);
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_key_last($matchingLines) also could return the type string which is incompatible with the documented return type boolean|integer.
Loading history...
168
        }
169
170
        return false;
171
    }
172
}
173