Passed
Pull Request — master (#415)
by Théo
02:54
created

PhpSettingsHandler::bumpMemoryLimit()   B

Complexity

Conditions 8
Paths 36

Size

Total Lines 45
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 45
rs 8.1954
c 0
b 0
f 0
cc 8
nc 36
nop 0
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 Assert\Assertion;
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;
0 ignored issues
show
Bug introduced by
The constant KevinGH\Box\BOX_MEMORY_LIMIT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function __construct(LoggerInterface $logger)
43
    {
44
        parent::__construct('box', '--ansi');
45
46
        $this->setLogger($logger);
47
        $this->logger = $logger;
48
49
        $this->pharReadonly = '1' === ini_get('phar.readonly');
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    protected function requiresRestart($isLoaded): bool
56
    {
57
        $this->bumpMemoryLimit();
58
59
        if ($this->pharReadonly) {
60
            $this->logger->debug('phar.readonly is enabled');
61
62
            return true;
63
        }
64
65
        $this->logger->debug('phar.readonly is disabled');
66
67
        return parent::requiresRestart($isLoaded);
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    protected function restart($command): void
74
    {
75
        // Disable phar.readonly if set
76
        $this->disablePharReadonly();
77
78
        parent::restart($command);
79
    }
80
81
    private function disablePharReadonly(): void
82
    {
83
        if (ini_get('phar.readonly')) {
84
            Assertion::notNull($this->tmpIni);
85
86
            append_to_file($this->tmpIni, 'phar.readonly=0'.PHP_EOL);
87
88
            $this->logger->debug('Configured `phar.readonly=0`');
89
        }
90
    }
91
92
    /**
93
     * @see https://github.com/composer/composer/blob/34c371f5f23e25eb9aa54ccc65136cf50930612e/bin/composer#L20-L50
94
     */
95
    private function bumpMemoryLimit(): void
96
    {
97
        $userDefinedMemoryLimit = self::getUserDefinedMemoryLimit();
98
99
        $memoryLimit = trim(ini_get('memory_limit'));
100
        $memoryLimitInBytes = '-1' === $memoryLimit ? -1 : memory_to_bytes($memoryLimit);
101
102
        // Whether or not the memory limit should be dumped
103
        $bumpMemoryLimit = (
104
            null === $userDefinedMemoryLimit
105
            && -1 !== $memoryLimitInBytes
106
            && $memoryLimitInBytes < 1024 * 1024 * 512
107
        );
108
        // Whether or not the memory limit should be set to the user defined memory limit
109
        $setUserDefinedMemoryLimit = (
110
            null !== $userDefinedMemoryLimit
111
            && $memoryLimitInBytes !== $userDefinedMemoryLimit
112
        );
113
114
        if ($bumpMemoryLimit && false === $setUserDefinedMemoryLimit) {
115
            ini_set('memory_limit', '512M');
116
117
            $this->logger->debug(
118
                sprintf(
119
                    'Changed the memory limit from "%s" to "%s"',
120
                    format_size($memoryLimitInBytes, 0),
121
                    '512M'
122
                )
123
            );
124
        } elseif ($setUserDefinedMemoryLimit) {
125
            ini_set('memory_limit', (string) $userDefinedMemoryLimit);
126
127
            $this->logger->debug(
128
                sprintf(
129
                    'Changed the memory limit from "%s" to %s="%s"',
130
                    format_size($memoryLimitInBytes, 0),
131
                    BOX_MEMORY_LIMIT,
132
                    format_size($userDefinedMemoryLimit, 0)
0 ignored issues
show
Bug introduced by
It seems like $userDefinedMemoryLimit can also be of type null; however, parameter $size of KevinGH\Box\format_size() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

132
                    format_size(/** @scrutinizer ignore-type */ $userDefinedMemoryLimit, 0)
Loading history...
133
                )
134
            );
135
        } else {
136
            $this->logger->debug(
137
                sprintf(
138
                    'Current memory limit: "%s"',
139
                    format_size($memoryLimitInBytes, 0)
140
                )
141
            );
142
        }
143
    }
144
145
    /**
146
     * @return null|int
147
     */
148
    private static function getUserDefinedMemoryLimit(): ?int
149
    {
150
        $memoryLimit = getenv(BOX_MEMORY_LIMIT);
151
152
        if (false === $memoryLimit) {
153
            $memoryLimitInBytes = null;
154
        } elseif ('-1' === $memoryLimit) {
155
            $memoryLimitInBytes = -1;
156
        } else {
157
            $memoryLimitInBytes = memory_to_bytes($memoryLimit);
158
        }
159
160
        return $memoryLimitInBytes;
161
    }
162
}
163