Passed
Push — master ( d8a9f3...906de1 )
by Théo
02:24
created

src/Console/Php/PhpSettingsHandler.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Console\Php;
16
17
use Composer\XdebugHandler\Process;
18
use Composer\XdebugHandler\XdebugHandler;
19
use function getenv;
20
use function ini_get;
21
use function ini_set;
22
use const KevinGH\Box\BOX_MEMORY_LIMIT;
23
use function KevinGH\Box\FileSystem\append_to_file;
24
use function KevinGH\Box\format_size;
25
use function KevinGH\Box\memory_to_bytes;
26
use const PHP_EOL;
27
use Psr\Log\LoggerInterface;
28
use function sprintf;
29
use function trim;
30
31
/**
32
 * @private
33
 */
34
final class PhpSettingsHandler extends XdebugHandler
35
{
36
    private $logger;
37
    private $pharReadonly;
38
    private $boxMemoryLimitInBytes;
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function __construct(LoggerInterface $logger)
44
    {
45
        parent::__construct('box', '--ansi');
46
47
        $this->setLogger($logger);
48
        $this->logger = $logger;
49
50
        $this->pharReadonly = '1' === ini_get('phar.readonly');
51
        $this->boxMemoryLimitInBytes = $this->checkMemoryLimit();
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function check(): void
58
    {
59
        parent::check();
60
61
        if (self::getRestartSettings()) {
62
            Process::setEnv('PHPRC', XdebugHandler::getRestartSettings()['tmpIni']);
63
            Process::setEnv('PHP_INI_SCAN_DIR', '');
64
        }
65
66
        // Bump the memory limit in the current process if necessary
67
        $this->bumpMemoryLimit();
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    protected function requiresRestart($isLoaded): bool
74
    {
75
        if ($this->pharReadonly) {
76
            $this->logger->debug('phar.readonly is enabled');
77
78
            return true;
79
        }
80
81
        $this->logger->debug('phar.readonly is disabled');
82
83
        return parent::requiresRestart($isLoaded);
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    protected function restart($command): void
90
    {
91
        // Disable phar.readonly if set
92
        $this->disablePharReadonly();
93
94
        // Bump the memory limit in the restated process if necessary
95
        $this->bumpMemoryLimit();
96
97
        parent::restart($command);
98
    }
99
100
    /**
101
     * @return false|int the desired memory limit for Box
102
     */
103
    private function checkMemoryLimit()
104
    {
105
        $memoryLimit = getenv(BOX_MEMORY_LIMIT);
106
107
        if (false === $memoryLimit) {
108
            $memoryLimitInBytes = false;
109
        } elseif ('-1' === $memoryLimit) {
110
            $memoryLimitInBytes = -1;
111
        } else {
112
            $memoryLimitInBytes = memory_to_bytes($memoryLimit);
113
        }
114
115
        return $memoryLimitInBytes;
116
    }
117
118
    private function disablePharReadonly(): void
119
    {
120
        if (ini_get('phar.readonly')) {
121
            append_to_file($this->tmpIni, 'phar.readonly=0'.PHP_EOL);
122
123
            $this->logger->debug('Configured `phar.readonly=0`');
124
        }
125
    }
126
127
    /**
128
     * @see https://github.com/composer/composer/blob/34c371f5f23e25eb9aa54ccc65136cf50930612e/bin/composer#L20-L50
129
     */
130
    private function bumpMemoryLimit(): void
131
    {
132
        $memoryLimit = trim(ini_get('memory_limit'));
133
        $memoryLimitInBytes = '-1' === $memoryLimit ? -1 : memory_to_bytes($memoryLimit);
134
135
        $bumpMemoryLimit = false === $this->boxMemoryLimitInBytes && -1 !== $memoryLimitInBytes && $memoryLimitInBytes < 1024 * 1024 * 512;
136
        $setUserDefinedMemoryLimit = $this->boxMemoryLimitInBytes && $memoryLimitInBytes !== $this->boxMemoryLimitInBytes;
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->boxMemoryLimitInBytes of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
137
138
        if ($bumpMemoryLimit && false === $setUserDefinedMemoryLimit) {
139
            if ($this->tmpIni) {
140
                // Is for the restarted process
141
                append_to_file($this->tmpIni, 'memory_limit=512M'.PHP_EOL);
142
            } else {
143
                // Is for the current process
144
                ini_set('memory_limit', '512M');
145
            }
146
147
            $this->logger->debug(
148
                sprintf(
149
                    'Changed the memory limit from "%s" to "%s"',
150
                    format_size($memoryLimitInBytes, 0),
151
                    '512M'
152
                )
153
            );
154
        } elseif ($setUserDefinedMemoryLimit) {
155
            if ($this->tmpIni) {
156
                // Is for the restarted process
157
                append_to_file($this->tmpIni, 'memory_limit='.$this->boxMemoryLimitInBytes.PHP_EOL);
158
            } else {
159
                // Is for the current process
160
                ini_set('memory_limit', (string) $this->boxMemoryLimitInBytes);
161
            }
162
163
            $this->logger->debug(
164
                sprintf(
165
                    'Changed the memory limit from "%s" to %s="%s"',
166
                    format_size($memoryLimitInBytes, 0),
167
                    BOX_MEMORY_LIMIT,
168
                    format_size($this->boxMemoryLimitInBytes, 0)
169
                )
170
            );
171
        } else {
172
            $this->logger->debug(
173
                sprintf(
174
                    'Current memory limit: "%s"',
175
                    format_size($memoryLimitInBytes, 0)
176
                )
177
            );
178
        }
179
    }
180
}
181