Passed
Push — renovate/php-8.x ( bfb893...222c0a )
by
unknown
02:20
created

PhpVersionDetector::detectPhpVersion()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 20
c 1
b 0
f 1
nc 3
nop 2
dl 0
loc 33
rs 9.6
1
<?php
2
3
/**
4
 * This file is part of the sj-i/php-profiler package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace PhpProfiler\Lib\PhpProcessReader;
15
16
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
17
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendArray;
18
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
19
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendModuleEntry;
20
use PhpProfiler\Lib\PhpInternals\ZendTypeReader;
21
use PhpProfiler\Lib\PhpInternals\ZendTypeReaderCreator;
22
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderInterface;
23
use PhpProfiler\Lib\Process\Pointer\Dereferencer;
24
use PhpProfiler\Lib\Process\Pointer\Pointer;
25
use PhpProfiler\Lib\Process\Pointer\RemoteProcessDereferencer;
26
use PhpProfiler\Lib\Process\ProcessSpecifier;
27
use Webmozart\Assert\Assert;
28
29
class PhpVersionDetector
30
{
31
    private ?ZendTypeReader $zend_type_reader = null;
32
33
    public function __construct(
34
        private PhpGlobalsFinder $php_globals_finder,
35
        private MemoryReaderInterface $memory_reader,
36
        private ZendTypeReaderCreator $zend_type_reader_creator,
37
    ) {
38
    }
39
40
    /**
41
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
42
     */
43
    private function getTypeReader(string $php_version): ZendTypeReader
44
    {
45
        if (is_null($this->zend_type_reader)) {
46
            $this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
47
        }
48
        return $this->zend_type_reader;
49
    }
50
51
    /**
52
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
53
     */
54
    private function getDereferencer(int $pid, string $php_version): Dereferencer
55
    {
56
        return new RemoteProcessDereferencer(
57
            $this->memory_reader,
58
            new ProcessSpecifier($pid),
59
            new ZendCastedTypeProvider(
60
                $this->getTypeReader($php_version),
61
            )
62
        );
63
    }
64
65
    /** @return TargetPhpSettings<value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>> */
66
    public function decidePhpVersion(
67
        ProcessSpecifier $process_specifier,
68
        TargetPhpSettings $target_php_settings,
69
    ): TargetPhpSettings {
70
        if ($target_php_settings->php_version !== 'auto') {
71
            /** @var TargetPhpSettings<value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>> */
72
            return $target_php_settings;
73
        }
74
        $module_registry_address = $this->php_globals_finder->findModuleRegistry(
75
            $process_specifier,
76
            $target_php_settings,
77
        );
78
        $version = null;
79
        if (!is_null($module_registry_address)) {
80
            $version = $this->detectPhpVersion($process_specifier->pid, $module_registry_address);
81
        }
82
83
        if (is_null($version)) {
84
            /** @var value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $version */
85
            $version = 'v' . PHP_MAJOR_VERSION . PHP_MINOR_VERSION;
86
            Assert::true(ZendTypeReader::isSupported($version));
87
        }
88
89
        return new TargetPhpSettings(
90
            php_regex: $target_php_settings->php_regex,
91
            libpthread_regex: $target_php_settings->libpthread_regex,
92
            php_version: $version,
93
            php_path: $target_php_settings->php_path,
94
            libpthread_path: $target_php_settings->libpthread_path,
95
        );
96
    }
97
98
    /** @return value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>|null */
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader:...UPPORTED_VERSIONS>|null at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>|null.
Loading history...
99
    public function detectPhpVersion(
100
        int $pid,
101
        int $module_registry_address
102
    ): ?string {
103
        $fake_php_version = ZendTypeReader::defaultVersion();
104
        $dereferencer = $this->getDereferencer($pid, $fake_php_version);
105
        $module_registry = $this->getModuleRegistry(
106
            $module_registry_address,
107
            $fake_php_version,
108
            $dereferencer
109
        );
110
        $module_registry_entry_bucket = $module_registry->findByKey($dereferencer, 'standard');
111
        if (is_null($module_registry_entry_bucket)) {
112
            return null;
113
        }
114
        $module_registry_entry_pointer = $module_registry_entry_bucket->val;
115
        $module_entry_pointer = new Pointer(
116
            ZendModuleEntry::class,
117
            $module_registry_entry_pointer->value->lval,
118
            $this->getTypeReader($fake_php_version)->sizeOf('zend_module_entry')
119
        );
120
        /**
121
         * @var ZendModuleEntry $basic_module_entry
122
         * @psalm-ignore-var
123
         */
124
        $basic_module_entry = $dereferencer->deref($module_entry_pointer);
125
        $version = $basic_module_entry->getVersion($dereferencer);
126
        $result_string = 'v' . str_replace('.', '', $version);
127
        if (ZendTypeReader::isSupported($result_string)) {
128
            /** @var value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> */
129
            return $result_string;
130
        }
131
        return null;
132
    }
133
134
    /**
135
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $fake_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
136
     */
137
    private function getModuleRegistry(
138
        int $module_registry_address,
139
        string $fake_php_version,
140
        Dereferencer $dereferencer
141
    ): ZendArray {
142
        /** @var value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $fake_php_version */
143
        $pointer = new Pointer(
144
            ZendArray::class,
145
            $module_registry_address,
146
            $this->getTypeReader($fake_php_version)->sizeOf('zend_array')
147
        );
148
        return $dereferencer->deref($pointer);
149
    }
150
}
151