Completed
Push — master ( 44ffb5...57c17f )
by Taha
07:31
created

XdebugToggle::getExtensionStatus()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.8657
c 0
b 0
f 0
cc 6
nc 12
nop 0
1
<?php
2
3
namespace Tpaksu\XdebugToggle\Commands;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Str;
8
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Process\Process;
11
12
class XdebugToggle extends Command
13
{
14
    /**
15
     * The complete line containing "*_extension=*xdebug*"
16
     *
17
     * @var string
18
     */
19
    protected $extensionLine;
20
21
    /**
22
     * Extension active status
23
     *
24
     * @var boolean
25
     */
26
    protected $extensionStatus;
27
28
    /**
29
     * The configuration written in php.ini for XDebug
30
     *
31
     * @var array
32
     */
33
    protected $extensionSettings;
34
35
    /**
36
     * Path of the Loaded INI file
37
     *
38
     * @var string
39
     */
40
    protected $iniPath;
41
42
    /**
43
     * Debug mode active flag
44
     *
45
     * @var boolean
46
     */
47
    protected $debug;
48
49
    /**
50
     * The command signature
51
     *
52
     * @var string
53
     */
54
    protected $signature = 'xdebug {status : "on" or "off" to enable/disable XDebug}';
55
56
    /**
57
     * The command description
58
     *
59
     * @var string
60
     */
61
    protected $description = 'Enables or disables XDebug extension';
62
63
    /**
64
     * Class constructor
65
     */
66
    public function __construct()
67
    {
68
        parent::__construct();
69
        $this->debug = false;
70
    }
71
72
    /**
73
     * The method that handles the command
74
     */
75
    public function handle()
76
    {
77
        // Define custom format for bold text
78
        $style = new OutputFormatterStyle('default', 'default', array('bold'));
79
        $this->output->getFormatter()->setStyle('bold', $style);
80
81
        // Get the verbosity level to set debug mode flag
82
        $verbosityLevel = $this->getOutput()->getVerbosity();
83
        if ($verbosityLevel > OutputInterface::VERBOSITY_DEBUG) {
84
            $this->debug = true;
85
        }
86
87
        // Get XDebug desired status from the command line arguments
88
        $desiredStatus = $this->argument("status");
89
90
        if ($this->debug) {
91
            echo "Desired Status: $desiredStatus\n";
92
        }
93
94
        // validate desired XDebug status
95
        if (!in_array($desiredStatus, ["on", "off"])) {
96
            $this->line("Status should be \"on\" or \"off\". Other values are not accepted.", "fg=red;bold");
97
            return false;
98
        }
99
100
        // Retrieve the INI path to the global variable
101
        $this->getIniPath();
102
103
        // If we can't retrieve the loaded INI path, bail out
104
        if ($this->iniPath == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->iniPath of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
105
            $this->line("Can't get php.ini file path from phpinfo() output.
106
            Make sure that the function is allowed inside your php.ini configuration.", "bold");
107
            return false;
108
        }
109
110
        // Get the XDebug extension information from the INI file
111
        $this->getXDebugStatus();
112
113
        // prepare variables for comparison and output
114
        $currentStatus = $this->extensionStatus ? "on" : "off";
115
        $styledStatus = $this->extensionStatus ? "<fg=green;bold>on" : "<fg=red;bold>off";
116
117
        // print current status to the user
118
        $this->line("<fg=yellow>Current XDebug Status: $styledStatus</>");
119
120
        // if the desired status and current status are the same, we don't need to alter anything
121
        // inform the user and exit
122
        if ($currentStatus === $desiredStatus) {
123
            $this->line("<fg=green>Already at the desired state. No action has been taken.</>");
124
            return false;
125
        }
126
127
        // we need to alter the status to the new one. Do it!
128
        $this->setXDebugStatus($desiredStatus);
0 ignored issues
show
Documentation introduced by
$desiredStatus is of type string|array|null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
    }
130
131
    /**
132
     * Sets the new XDebug extension status
133
     *
134
     * @param   boolean  $status  Whether the extension should be active or not
135
     *
136
     * @return  void
137
     */
138
    private function setXDebugStatus($status)
139
    {
140
        // inform the user about the current operation
141
        $this->line("<bold>Setting status to $status</bold>");
142
143
        // read the ini file
144
        $contents = file_get_contents($this->iniPath);
145
146
        if ($this->debug) {
147
            echo "status: $status\n";
148
            echo "line: " . $this->extensionLine . "\n";
149
            echo "new: " . trim($this->extensionLine, ";") . "\n";
150
        }
151
152
        // replace the "zend_extension=*xdebug.*" line with the active/passive equivalent
153
        switch ($status) {
154
            case 'on':
155
                $contents = str_replace($this->extensionLine, trim($this->extensionLine, ";"), $contents);
156
                break;
157
158
            default:
159
                $contents = str_replace($this->extensionLine, ";" . $this->extensionLine, $contents);
160
                break;
161
        }
162
163
        // rewrite the php.ini file
164
        file_put_contents($this->iniPath, $contents);
165
166
        // restart the service to put the changes in effect
167
        $this->restartServices();
168
    }
169
170
    /**
171
     * Retrieves the INI path from php.ini file
172
     *
173
     * @return void
174
     */
175
    private function getIniPath()
176
    {
177
        $this->iniPath = php_ini_loaded_file();
178
    }
179
180
    /**
181
     * Gets the XDebug status and related configuration from the loaded php.ini file
182
     */
183
    private function getXDebugStatus()
184
    {
185
        // get the extension status
186
        $this->getExtensionStatus();
187
188
        // get extemsion settings
189
        $this->getExtensionSettings();
190
    }
191
192
    /**
193
     * Reads the extension status from PHP ini file
194
     *
195
     * @return void
196
     */
197
    private function getExtensionStatus()
198
    {
199
        // read the extension line from file,
200
        // can't use parse_ini_file here because the keyed array overwrites "extension" lines and keeps the last one
201
        $this->extensionLine = collect(file_get_contents($this->iniPath))
202
            ->explode("\n")
203
            ->filter(function($line){
204
                return Str::contains($line, "extension=") && Str::contains($line, "xdebug");
205
            })
206
            ->first();
207
208
        $this->extensionLine = trim($this->extensionLine ?? "");
209
210
        if ($this->debug) {
211
            echo "line: " . $this->extensionLine . "\n";
212
        }
213
214
        if (strlen($this->extensionLine) > 0) {
215
            $this->extensionStatus = $this->extensionLine[0] == ";" ? false : true;
216
        } else {
217
            $this->extensionStatus = false;
218
        }
219
220
        if ($this->debug) {
221
            echo "ext.status: " . $this->extensionStatus . "\n";
222
        }
223
    }
224
225
    /**
226
     * Reads the extension settings from PHP ini file
227
     *
228
     * @return void
229
     */
230
    private function getExtensionSettings()
231
    {
232
        $settings = collect(parse_ini_file($this->iniPath))->filter(function ($setting, $key) {
233
            return Str::startsWith($key, "xdebug.");
234
        });
235
        $this->extensionSettings = $settings->toArray();
236
    }
237
238
    /**
239
     * Restarts the services that takes the modification into effect
240
     *
241
     * @return void
242
     */
243
    private function restartServices()
244
    {
245
        /**
246
         * Define a global outputter to display command output to user
247
         *
248
         * @param   string  $type  The type of the output
249
         * @param   string  $data  The output
250
         *
251
         * @return  void
252
         */
253
        $output = function ($type, $data) {
254
            $this->info($data);
255
        };
256
        // run the command(s) needed to restart the service
257
        (new Process([env("XDEBUG_SERVICE_RESTART_COMMAND", "valet restart nginx")]))->run($output);
258
        // display the new extension status
259
        (new Process(["php --ri xdebug"]))->run($output);
260
    }
261
}
262