Passed
Push — remove-current-function ( aa4d5a )
by Shinji
13:10
created

CallTraceReader::readCallTrace()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 36
c 0
b 0
f 0
nc 9
nop 4
dl 0
loc 61
rs 8.0995

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\PhpMemoryReader;
15
16
use PhpProfiler\Lib\PhpInternals\Opcodes\OpcodeFactory;
17
use PhpProfiler\Lib\PhpInternals\Types\Zend\Opline;
18
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
19
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendExecuteData;
20
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
21
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendFunction;
22
use PhpProfiler\Lib\PhpInternals\Types\Zend\ZendOp;
23
use PhpProfiler\Lib\PhpInternals\ZendTypeReader;
24
use PhpProfiler\Lib\PhpInternals\ZendTypeReaderCreator;
25
use PhpProfiler\Lib\PhpProcessReader\CallFrame;
26
use PhpProfiler\Lib\PhpProcessReader\CallTrace;
27
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderInterface;
28
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderException;
29
use PhpProfiler\Lib\Process\Pointer\Dereferencer;
30
use PhpProfiler\Lib\Process\Pointer\Pointer;
31
use PhpProfiler\Lib\Process\Pointer\RemoteProcessDereferencer;
32
use PhpProfiler\Lib\Process\ProcessSpecifier;
33
34
final class CallTraceReader
35
{
36
    private ?ZendTypeReader $zend_type_reader = null;
37
38
    public function __construct(
39
        private MemoryReaderInterface $memory_reader,
40
        private ZendTypeReaderCreator $zend_type_reader_creator,
41
        private OpcodeFactory $opcode_factory,
42
    ) {
43
    }
44
45
    /**
46
     * @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...
47
     */
48
    public function getTypeReader(string $php_version): ZendTypeReader
49
    {
50
        if (is_null($this->zend_type_reader)) {
51
            $this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
52
        }
53
        return $this->zend_type_reader;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->zend_type_reader could return the type null which is incompatible with the type-hinted return PhpProfiler\Lib\PhpInternals\ZendTypeReader. Consider adding an additional type-check to rule them out.
Loading history...
54
    }
55
56
    /**
57
     * @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...
58
     */
59
    private function getDereferencer(int $pid, string $php_version): Dereferencer
60
    {
61
        return new RemoteProcessDereferencer(
62
            $this->memory_reader,
63
            new ProcessSpecifier($pid),
64
            new ZendCastedTypeProvider(
65
                $this->getTypeReader($php_version),
66
            )
67
        );
68
    }
69
70
    /**
71
     * @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...
72
     */
73
    private function getExecutorGlobals(
74
        int $eg_address,
75
        string $php_version,
76
        Dereferencer $dereferencer
77
    ): ZendExecutorGlobals {
78
        $zend_type_reader = $this->getTypeReader($php_version);
79
        $eg_pointer = new Pointer(
80
            ZendExecutorGlobals::class,
81
            $eg_address,
82
            $zend_type_reader->sizeOf('zend_executor_globals')
83
        );
84
        return $dereferencer->deref($eg_pointer);
85
    }
86
87
    /**
88
     * @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...
89
     * @throws MemoryReaderException
90
     */
91
    public function readCallTrace(int $pid, string $php_version, int $executor_globals_address, int $depth): ?CallTrace
92
    {
93
        $dereferencer = $this->getDereferencer($pid, $php_version);
94
        $eg = $this->getExecutorGlobals($executor_globals_address, $php_version, $dereferencer);
95
        if (is_null($eg->current_execute_data)) {
96
            return null;
97
        }
98
        /**
99
         * @var ZendExecuteData $current_execute_data
100
         * @psalm-ignore-var
101
         */
102
        $current_execute_data = $dereferencer->deref($eg->current_execute_data);
103
104
        $stack = [];
105
        $stack[] = $current_execute_data;
106
        for ($i = 0; $i < $depth; $i++) {
107
            if (is_null($current_execute_data->prev_execute_data)) {
108
                break;
109
            }
110
            $current_execute_data = $dereferencer->deref($current_execute_data->prev_execute_data);
111
            $stack[] = $current_execute_data;
112
        }
113
114
        $result = [];
115
        foreach ($stack as $current_execute_data) {
116
            if (is_null($current_execute_data->func)) {
117
                $result[] = new CallFrame(
118
                    '',
119
                    '<unknown>',
120
                    '<unknown>',
121
                    null
122
                );
123
                continue;
124
            }
125
            /**
126
             * @var ZendFunction $current_function
127
             * @psalm-ignore-var
128
             */
129
            $current_function = $dereferencer->deref($current_execute_data->func);
130
131
            $function_name = $current_function->getFunctionName($dereferencer) ?? '<main>';
132
            $class_name = $current_function->getClassName($dereferencer) ?? '';
133
            $file_name = $current_function->getFileName($dereferencer) ?? '<unknown>';
134
135
            $opline = null;
136
            if ($file_name !== '<internal>' and !is_null($current_execute_data->opline)) {
137
                $opline = $this->readOpline(
138
                    $php_version,
139
                    $dereferencer->deref($current_execute_data->opline)
140
                );
141
            }
142
143
            $result[] = new CallFrame(
144
                $class_name,
145
                $function_name,
146
                $file_name,
147
                $opline
148
            );
149
        }
150
151
        return new CallTrace(...$result);
152
    }
153
154
    /**
155
     * @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...
156
     */
157
    private function readOpline(string $php_version, ZendOp $zend_op): Opline
158
    {
159
        return new Opline(
160
            $zend_op->op1,
161
            $zend_op->op2,
162
            $zend_op->result,
163
            $zend_op->extended_value,
164
            $zend_op->lineno,
165
            $this->opcode_factory->create($php_version, $zend_op->opcode),
166
            $zend_op->op1_type,
167
            $zend_op->op2_type,
168
            $zend_op->result_type
169
        );
170
    }
171
}
172