PhpVersionDetector   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 53
c 1
b 0
f 1
dl 0
loc 122
rs 10
wmc 12

6 Methods

Rating   Name   Duplication   Size   Complexity  
A detectPhpVersion() 0 33 3
A getDereferencer() 0 7 1
A getTypeReader() 0 6 2
A getModuleRegistry() 0 12 1
A decidePhpVersion() 0 31 4
A __construct() 0 5 1
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
        assert(isset($target_php_settings->libpthread_path));
90
91
        return new TargetPhpSettings(
92
            php_regex: $target_php_settings->php_regex,
93
            libpthread_regex: $target_php_settings->libpthread_regex,
94
            php_version: $version,
95
            php_path: $target_php_settings->php_path,
96
            libpthread_path: $target_php_settings->libpthread_path,
97
        );
98
    }
99
100
    /** @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...
101
    public function detectPhpVersion(
102
        int $pid,
103
        int $module_registry_address
104
    ): ?string {
105
        $fake_php_version = ZendTypeReader::defaultVersion();
106
        $dereferencer = $this->getDereferencer($pid, $fake_php_version);
107
        $module_registry = $this->getModuleRegistry(
108
            $module_registry_address,
109
            $fake_php_version,
110
            $dereferencer
111
        );
112
        $module_registry_entry_bucket = $module_registry->findByKey($dereferencer, 'standard');
113
        if (is_null($module_registry_entry_bucket)) {
114
            return null;
115
        }
116
        $module_registry_entry_pointer = $module_registry_entry_bucket->val;
117
        $module_entry_pointer = new Pointer(
118
            ZendModuleEntry::class,
119
            $module_registry_entry_pointer->value->lval,
120
            $this->getTypeReader($fake_php_version)->sizeOf('zend_module_entry')
121
        );
122
        /**
123
         * @var ZendModuleEntry $basic_module_entry
124
         * @psalm-ignore-var
125
         */
126
        $basic_module_entry = $dereferencer->deref($module_entry_pointer);
127
        $version = $basic_module_entry->getVersion($dereferencer);
128
        $result_string = 'v' . str_replace('.', '', $version);
129
        if (ZendTypeReader::isSupported($result_string)) {
130
            /** @var value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> */
131
            return $result_string;
132
        }
133
        return null;
134
    }
135
136
    /**
137
     * @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...
138
     */
139
    private function getModuleRegistry(
140
        int $module_registry_address,
141
        string $fake_php_version,
142
        Dereferencer $dereferencer
143
    ): ZendArray {
144
        /** @var value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $fake_php_version */
145
        $pointer = new Pointer(
146
            ZendArray::class,
147
            $module_registry_address,
148
            $this->getTypeReader($fake_php_version)->sizeOf('zend_array')
149
        );
150
        return $dereferencer->deref($pointer);
151
    }
152
}
153