MemoryLocationsCollector   F
last analyzed

Complexity

Total Complexity 147

Size/Duplication

Total Lines 1685
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 1037
c 4
b 0
f 0
dl 0
loc 1685
rs 1.452
wmc 147

62 Methods

Rating   Name   Duplication   Size   Complexity  
A hp$0 ➔ __construct() 0 3 1
A __construct() 0 5 1
A getTypeReader() 0 6 2
getMainChunkAddress() 0 14 ?
A hp$0 ➔ getMainChunkAddress() 0 14 2
collectClassDefinition() 0 133 ?
collectClassConstantsTable() 0 64 ?
A hp$0 ➔ collectPropertiesInfo() 0 39 4
A hp$0 ➔ collectZendObjectPointer() 0 28 3
collectGlobalConstants() 0 49 ?
collectZendObject() 0 94 ?
A hp$0 ➔ collectInternedStrings() 0 15 1
A hp$0 ➔ resolve() 0 30 1
collectClassDefinitionPointer() 0 21 ?
collectZendArray() 0 57 ?
collectZval() 0 81 ?
collectObjectsStore() 0 45 ?
collectUserFunctionDefinition() 0 176 ?
D hp$0 ➔ collectUserFunctionDefinition() 0 176 17
collectResourcePointer() 0 21 ?
A hp$0 ➔ collectGlobalVariables() 0 16 1
A hp$0 ➔ collectClassTable() 0 26 3
A hp$0 ➔ collectPhpReferencePointer() 0 36 4
collectPhpReferencePointer() 0 36 ?
A hp$0 ➔ getDereferencer() 0 44 1
collectZendArrayPointer() 0 25 ?
B hp$0 ➔ collectObjectsStore() 0 45 6
C hp$0 ➔ collectCallFrame() 0 91 12
collectZendFunctionPointer() 0 27 ?
collectCallFrame() 0 91 ?
A hp$0 ➔ collectZendArrayPointer() 0 25 3
collectClassTable() 0 26 ?
collectZendStringPointer() 0 26 ?
collectZendObjectPointer() 0 28 ?
A hp$0 ➔ collectResourcePointer() 0 21 3
C hp$0 ➔ collectZendObject() 0 94 8
A hp$0 ➔ collectClosure() 0 35 2
collectInternedStrings() 0 15 ?
C hp$0 ➔ collectZval() 0 81 11
collectIncludedFiles() 0 24 ?
A hp$0 ➔ collectCallFrames() 0 25 3
collectPropertiesInfo() 0 39 ?
collectClosure() 0 35 ?
A hp$0 ➔ collectFunctionTable() 0 40 2
collectZendFunction() 0 32 ?
A hp$0 ➔ collectZendStringPointer() 0 26 4
getDereferencer() 0 44 ?
collectFunctionTable() 0 40 ?
collectRealCallStackOnMemoryLimitViolation() 0 88 ?
B hp$0 ➔ collectZendArray() 0 57 4
A hp$0 ➔ collectZendFunctionPointer() 0 27 3
collectCallFrames() 0 25 ?
A hp$0 ➔ collectGlobalConstants() 0 49 4
A hp$0 ➔ collectZendFunction() 0 32 3
C hp$0 ➔ collectRealCallStackOnMemoryLimitViolation() 0 88 12
A hp$0 ➔ collectIncludedFiles() 0 24 2
A hp$0 ➔ collectClassDefinitionPointer() 0 21 2
collectAll() 0 207 ?
C hp$0 ➔ collectAll() 0 207 11
B hp$0 ➔ collectClassConstantsTable() 0 64 4
collectGlobalVariables() 0 16 ?
C hp$0 ➔ collectClassDefinition() 0 133 10

How to fix   Complexity   

Complex Class

Complex classes like MemoryLocationsCollector often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MemoryLocationsCollector, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of the reliforp/reli-prof 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 Reli\Lib\PhpProcessReader\PhpMemoryReader;
15
16
use Reli\Inspector\Settings\MemoryProfilerSettings\MemoryLimitErrorDetails;
17
use Reli\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
18
use Reli\Lib\Log\Log;
19
use Reli\Lib\PhpInternals\Types\Zend\Bucket;
0 ignored issues
show
Bug introduced by
The type Reli\Lib\PhpInternals\Types\Zend\Bucket was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use Reli\Lib\PhpInternals\Types\Zend\ZendArray;
0 ignored issues
show
Bug introduced by
The type Reli\Lib\PhpInternals\Types\Zend\ZendArray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Reli\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
22
use Reli\Lib\PhpInternals\Types\Zend\ZendClassConstant;
23
use Reli\Lib\PhpInternals\Types\Zend\ZendClassEntry;
24
use Reli\Lib\PhpInternals\Types\Zend\ZendClosure;
25
use Reli\Lib\PhpInternals\Types\Zend\ZendCompilerGlobals;
26
use Reli\Lib\PhpInternals\Types\Zend\ZendConstant;
27
use Reli\Lib\PhpInternals\Types\Zend\ZendExecuteData;
28
use Reli\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
29
use Reli\Lib\PhpInternals\Types\Zend\ZendFunction;
30
use Reli\Lib\PhpInternals\Types\Zend\ZendMmChunk;
31
use Reli\Lib\PhpInternals\Types\Zend\ZendObject;
32
use Reli\Lib\PhpInternals\Types\Zend\ZendObjectsStore;
33
use Reli\Lib\PhpInternals\Types\Zend\ZendReference;
34
use Reli\Lib\PhpInternals\Types\Zend\ZendResource;
35
use Reli\Lib\PhpInternals\Types\Zend\ZendString;
36
use Reli\Lib\PhpInternals\Types\Zend\Zval;
37
use Reli\Lib\PhpInternals\ZendTypeReader;
38
use Reli\Lib\PhpInternals\ZendTypeReaderCreator;
39
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameHeaderMemoryLocation;
40
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameVariableTableMemoryLocation;
41
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultPropertiesTableMemoryLocation;
42
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultStaticMembersTableMemoryLocation;
43
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DynamicFuncDefsTableMemoryLocation;
44
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\LocalVariableNameTableMemoryLocation;
45
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\MemoryLocations;
46
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ObjectsStoreMemoryLocation;
47
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\RuntimeCacheMemoryLocation;
48
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\StaticMembersTableMemoryLocation;
49
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\VmStackMemoryLocation;
50
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArenaMemoryLocation;
51
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArgInfosMemoryLocation;
52
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayMemoryLocation;
53
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableMemoryLocation;
54
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableOverheadMemoryLocation;
55
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassConstantMemoryLocation;
56
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassEntryMemoryLocation;
57
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendConstantMemoryLocation;
58
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmChunkMemoryLocation;
59
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmHugeListMemoryLocation;
60
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectHandlersMemoryLocation;
61
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectMemoryLocation;
62
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayBodyMemoryLocation;
63
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayHeaderMemoryLocation;
64
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendPropertyInfoMemoryLocation;
65
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendReferenceMemoryLocation;
66
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendResourceMemoryLocation;
67
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendStringMemoryLocation;
68
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfoContext;
69
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfosContext;
70
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementContext;
71
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementsContext;
72
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayHeaderContext;
73
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayPossibleOverheadContext;
74
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameContext;
75
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFramesContext;
76
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameVariableTableContext;
77
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantContext;
78
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantInfoContext;
79
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantsContext;
80
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassDefinitionContext;
81
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassEntryContext;
82
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassStaticPropertiesContext;
83
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClosureContext;
84
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultPropertiesTableContext;
85
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultStaticPropertiesContext;
86
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedClassesContext;
87
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedFunctionsContext;
88
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DynamicFuncDefsContext;
89
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\FunctionDefinitionContext;
90
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantContext;
91
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantsContext;
92
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalVariablesContext;
93
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\IncludedFilesContext;
94
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\InternalFunctionDefinitionContext;
95
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\LocalVariableNameTableContext;
96
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectContext;
97
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectPropertiesContext;
98
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectsStoreContext;
99
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\OpArrayContext;
100
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PhpReferenceContext;
101
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertiesInfoContext;
102
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertyInfoContext;
103
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ReferenceContext;
104
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ResourceContext;
105
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\RuntimeCacheContext;
106
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ScalarValueContext;
107
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\StringContext;
108
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\TopReferenceContext;
109
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\UserFunctionDefinitionContext;
110
use Reli\Lib\PhpProcessReader\PhpZendMemoryManagerChunkFinder;
111
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;
112
use Reli\Lib\Process\Pointer\Dereferencer;
113
use Reli\Lib\Process\Pointer\PointedTypeResolver;
114
use Reli\Lib\Process\Pointer\Pointer;
115
use Reli\Lib\Process\Pointer\RemoteProcessDereferencer;
116
use Reli\Lib\Process\ProcessSpecifier;
117
118
/** @psalm-import-type VersionDecided from TargetPhpSettings */
119
final class MemoryLocationsCollector
120
{
121
    private ?ZendTypeReader $zend_type_reader = null;
122
    private ?UserFunctionDefinitionContext $memory_limit_error_function_context = null;
123
124
    public function __construct(
125
        private MemoryReaderInterface $memory_reader,
126
        private ZendTypeReaderCreator $zend_type_reader_creator,
127
        private PhpZendMemoryManagerChunkFinder $chunk_finder,
128
    ) {
129
    }
130
131
    /**
132
     * @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...
133
     */
134
    public function getTypeReader(string $php_version): ZendTypeReader
135
    {
136
        if (is_null($this->zend_type_reader)) {
137
            $this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
138
        }
139
        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 Reli\Lib\PhpInternals\ZendTypeReader. Consider adding an additional type-check to rule them out.
Loading history...
140
    }
141
142
    /**
143
     * @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...
144
     */
145
    private function getDereferencer(int $pid, string $php_version): Dereferencer
146
    {
147
        return new RemoteProcessDereferencer(
148
            $this->memory_reader,
149
            new ProcessSpecifier($pid),
150
            new ZendCastedTypeProvider(
151
                $this->getTypeReader($php_version),
152
            ),
153
            new class ($php_version) implements PointedTypeResolver {
154
                public function __construct(
155
                    private string $php_version,
156
                ) {
157
                }
158
159
                public function resolve(string $type_name): string
160
                {
161
                    return match ($this->php_version) {
162
                        ZendTypeReader::V70,
163
                        ZendTypeReader::V71,
164
                        ZendTypeReader::V72 => match ($type_name) {
165
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Bucket::class,
166
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V70\ZendArray::class,
167
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Zval::class,
168
                            default => $type_name,
169
                        },
170
                        ZendTypeReader::V73 => match ($type_name) {
171
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Bucket::class,
172
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V73\ZendArray::class,
173
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Zval::class,
174
                            default => $type_name,
175
                        },
176
                        ZendTypeReader::V74 => match ($type_name) {
177
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Bucket::class,
178
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V74\ZendArray::class,
179
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Zval::class,
180
                            default => $type_name,
181
                        },
182
                        ZendTypeReader::V80,
183
                        ZendTypeReader::V81 => match ($type_name) {
184
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V80\ZendArray::class,
185
                            default => $type_name,
186
                        },
187
                        ZendTypeReader::V82,
188
                        ZendTypeReader::V83 => $type_name,
189
                    };
190
                }
191
            }
192
        );
193
    }
194
195
    /** @param TargetPhpSettings<VersionDecided> $target_php_settings */
196
    private function getMainChunkAddress(
197
        ProcessSpecifier $process_specifier,
198
        TargetPhpSettings $target_php_settings,
199
        Dereferencer $dereferencer,
200
    ): int {
201
        $chunk_address = $this->chunk_finder->findAddress(
202
            $process_specifier,
203
            $target_php_settings,
204
            $dereferencer,
205
        );
206
        if (is_null($chunk_address)) {
207
            throw new \RuntimeException('chunk address not found');
208
        }
209
        return $chunk_address;
210
    }
211
212
    /** @param TargetPhpSettings<VersionDecided> $target_php_settings */
213
    public function collectAll(
214
        ProcessSpecifier $process_specifier,
215
        TargetPhpSettings $target_php_settings,
216
        int $eg_address,
217
        int $cg_address,
218
        ?MemoryLimitErrorDetails $memory_limit_error_details = null,
219
    ): CollectedMemories {
220
        $pid = $process_specifier->pid;
221
        $php_version = $target_php_settings->php_version;
0 ignored issues
show
Documentation Bug introduced by
It seems like $target_php_settings->php_version of type string is incompatible with the declared type Reli\Inspector\Settings\TargetPhpSettings\TVersion of property $php_version.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
222
        $dereferencer = $this->getDereferencer($pid, $php_version);
223
        $zend_type_reader = $this->zend_type_reader_creator->create($php_version);
224
225
        $main_chunk_header_pointer = new Pointer(
226
            ZendMmChunk::class,
227
            $this->getMainChunkAddress(
228
                $process_specifier,
229
                $target_php_settings,
230
                $dereferencer,
231
            ),
232
            $zend_type_reader->sizeOf('zend_mm_chunk'),
233
        );
234
235
        $memory_locations = new MemoryLocations();
236
        $chunk_memory_locations = new MemoryLocations();
237
238
        $zend_mm_main_chunk = $dereferencer->deref($main_chunk_header_pointer);
239
        foreach ($zend_mm_main_chunk->iterateChunks($dereferencer) as $chunk) {
240
            $chunk_memory_location = ZendMmChunkMemoryLocation::fromZendMmChunk($chunk);
241
            $chunk_memory_locations->add(
242
                $chunk_memory_location
243
            );
244
        }
245
        $huge_memory_locations = new MemoryLocations();
246
        foreach ($zend_mm_main_chunk->heap_slot->iterateHugeList($dereferencer) as $huge_list) {
247
            $huge_memory_locations->add(
248
                ZendMmChunkMemoryLocation::fromZendMmHugeList($huge_list)
249
            );
250
            $memory_locations->add(
251
                ZendMmHugeListMemoryLocation::fromZendMmHugeList($huge_list)
252
            );
253
        }
254
255
        $memory_get_usage_size = $zend_mm_main_chunk->heap_slot->size;
256
        $memory_get_usage_real_size = $zend_mm_main_chunk->heap_slot->real_size;
257
        $cached_chunks_size = $zend_mm_main_chunk->heap_slot->cached_chunks_count * ZendMmChunk::SIZE;
258
259
        $eg_pointer = new Pointer(
260
            ZendExecutorGlobals::class,
261
            $eg_address,
262
            $zend_type_reader->sizeOf('zend_executor_globals')
263
        );
264
        $cg_pointer = new Pointer(
265
            ZendCompilerGlobals::class,
266
            $cg_address,
267
            $zend_type_reader->sizeOf('zend_compiler_globals')
268
        );
269
270
        $compiler_arena_memory_locations = new MemoryLocations();
271
        /** @var ZendCompilerGlobals $cg */
272
        $cg = $dereferencer->deref($cg_pointer);
273
        if ($cg->arena !== null) {
274
            $arena_root = $dereferencer->deref($cg->arena);
275
            foreach ($arena_root->iterateChain($dereferencer) as $arena) {
276
                $compiler_arena_memory_locations->add(
277
                    ZendArenaMemoryLocation::fromZendArena($arena)
278
                );
279
            }
280
        }
281
282
        if ($cg->ast_arena !== null) {
283
            $ast_arena_root = $dereferencer->deref($cg->ast_arena);
284
            foreach ($ast_arena_root->iterateChain($dereferencer) as $ast_arena) {
285
                $compiler_arena_memory_locations->add(
286
                    ZendArenaMemoryLocation::fromZendArena($ast_arena)
287
                );
288
            }
289
        }
290
291
        /** @var ZendExecutorGlobals $eg */
292
        $eg = $dereferencer->deref($eg_pointer);
293
294
        $vm_stack_memory_locations = new MemoryLocations();
295
        if (!is_null($eg->vm_stack)) {
296
            $vm_stack_curent = $dereferencer->deref($eg->vm_stack);
297
            foreach ($vm_stack_curent->iterateStackChain($dereferencer) as $vm_stack) {
298
                $vm_stack_memory_locations->add(
299
                    VmStackMemoryLocation::fromZendVmStack($vm_stack),
300
                );
301
            }
302
        }
303
304
        $context_pools = ContextPools::createDefault();
305
306
        $included_files_context = $this->collectIncludedFiles(
307
            $eg->included_files,
308
            $dereferencer,
309
            $memory_locations,
310
            $context_pools,
311
        );
312
313
        $interned_strings_context = $this->collectInternedStrings(
314
            $cg->interned_strings,
315
            $cg->map_ptr_base,
316
            $dereferencer,
317
            $zend_type_reader,
318
            $memory_locations,
319
            $context_pools,
320
        );
321
322
        assert(!is_null($eg->function_table));
323
        assert(!is_null($eg->class_table));
324
        assert(!is_null($eg->zend_constants));
325
326
        $function_table = $dereferencer->deref($eg->function_table);
327
        $class_table = $dereferencer->deref($eg->class_table);
328
        $zend_constants = $dereferencer->deref($eg->zend_constants);
329
330
        $global_variables_context = $this->collectGlobalVariables(
331
            $eg->symbol_table,
332
            $cg->map_ptr_base,
333
            $dereferencer,
334
            $zend_type_reader,
335
            $memory_locations,
336
            $context_pools,
337
        );
338
339
        $call_frames_context = $this->collectCallFrames(
340
            $eg,
341
            $cg->map_ptr_base,
342
            $dereferencer,
343
            $zend_type_reader,
344
            $memory_locations,
345
            $context_pools,
346
        );
347
348
        $defined_functions_context = $this->collectFunctionTable(
349
            $function_table,
350
            $cg->map_ptr_base,
351
            $dereferencer,
352
            $zend_type_reader,
353
            $memory_locations,
354
            $context_pools,
355
            $memory_limit_error_details,
356
        );
357
358
        $defined_classes_context = $this->collectClassTable(
359
            $class_table,
360
            $cg->map_ptr_base,
361
            $dereferencer,
362
            $zend_type_reader,
363
            $memory_locations,
364
            $context_pools,
365
        );
366
367
        $global_constants_context = $this->collectGlobalConstants(
368
            $zend_constants,
369
            $cg->map_ptr_base,
370
            $dereferencer,
371
            $zend_type_reader,
372
            $memory_locations,
373
            $context_pools,
374
        );
375
376
        $objects_store_context = $this->collectObjectsStore(
377
            $eg->objects_store,
378
            $cg->map_ptr_base,
379
            $dereferencer,
380
            $zend_type_reader,
381
            $memory_locations,
382
            $context_pools,
383
        );
384
385
        if ($memory_limit_error_details and !is_null($this->memory_limit_error_function_context)) {
386
            $call_frames_context = $this->collectRealCallStackOnMemoryLimitViolation(
387
                $this->memory_limit_error_function_context,
388
                $memory_limit_error_details->max_challenge_depth,
389
                $call_frames_context,
390
                $eg,
391
                $cg->map_ptr_base,
392
                $dereferencer,
393
                $zend_type_reader,
394
                $memory_locations,
395
                $context_pools,
396
            );
397
        }
398
399
        $top_reference_context = new TopReferenceContext(
400
            $call_frames_context,
401
            $global_variables_context,
402
            $defined_functions_context,
403
            $defined_classes_context,
404
            $global_constants_context,
405
            $included_files_context,
406
            $interned_strings_context,
407
            $objects_store_context,
408
        );
409
410
        return new CollectedMemories(
411
            $chunk_memory_locations,
412
            $huge_memory_locations,
413
            $vm_stack_memory_locations,
414
            $compiler_arena_memory_locations,
415
            $cached_chunks_size,
416
            $memory_locations,
417
            $top_reference_context,
418
            $memory_get_usage_size,
419
            $memory_get_usage_real_size,
420
        );
421
    }
422
423
    public function collectZval(
424
        Zval $zval,
425
        int $map_ptr_base,
426
        Dereferencer $dereferencer,
427
        ZendTypeReader $zend_type_reader,
428
        MemoryLocations $memory_locations,
429
        ContextPools $context_pools
430
    ): ?ReferenceContext {
431
        if ($zval->isArray()) {
432
            assert(!is_null($zval->value->arr));
433
            return $this->collectZendArrayPointer(
434
                $zval->value->arr,
435
                $map_ptr_base,
436
                $memory_locations,
437
                $dereferencer,
438
                $zend_type_reader,
439
                $context_pools,
440
            );
441
        } elseif ($zval->isObject()) {
442
            assert(!is_null($zval->value->obj));
443
            return $this->collectZendObjectPointer(
444
                $zval->value->obj,
445
                $map_ptr_base,
446
                $memory_locations,
447
                $dereferencer,
448
                $zend_type_reader,
449
                $context_pools,
450
            );
451
        } elseif ($zval->isString()) {
452
            assert(!is_null($zval->value->str));
453
            return $this->collectZendStringPointer(
454
                $zval->value->str,
455
                $memory_locations,
456
                $dereferencer,
457
                $context_pools,
458
            );
459
        } elseif (
460
            $zval->isBool()
461
            or $zval->isLong()
462
            or $zval->isDouble()
463
            or $zval->isNull()
464
        ) {
465
            return match ($zval->getType()) {
466
                'IS_TRUE' => new ScalarValueContext(true),
467
                'IS_FALSE' => new ScalarValueContext(false),
468
                'IS_LONG' => new ScalarValueContext($zval->value->lval),
469
                'IS_DOUBLE' => new ScalarValueContext($zval->value->dval),
470
                'IS_NULL' => new ScalarValueContext(null),
471
            };
472
        } elseif ($zval->isReference()) {
473
            assert(!is_null($zval->value->ref));
474
            return $this->collectPhpReferencePointer(
475
                $zval->value->ref,
476
                $map_ptr_base,
477
                $memory_locations,
478
                $dereferencer,
479
                $zend_type_reader,
480
                $context_pools,
481
            );
482
        } elseif ($zval->isResource()) {
483
            assert(!is_null($zval->value->res));
484
            return $this->collectResourcePointer(
485
                $zval->value->res,
486
                $memory_locations,
487
                $dereferencer,
488
                $context_pools,
489
            );
490
        } elseif ($zval->isIndirect()) {
491
            $zval = $dereferencer->deref(
492
                $zval->value->getAsPointer(Zval::class, $zend_type_reader->sizeOf('zval'))
493
            );
494
            return $this->collectZval(
495
                $zval,
496
                $map_ptr_base,
497
                $dereferencer,
498
                $zend_type_reader,
499
                $memory_locations,
500
                $context_pools,
501
            );
502
        }
503
        return null;
504
    }
505
506
    /** @param Pointer<ZendResource> $pointer */
507
    public function collectResourcePointer(
508
        Pointer $pointer,
509
        MemoryLocations $memory_locations,
510
        Dereferencer $dereferencer,
511
        ContextPools $context_pools
512
    ): ResourceContext {
513
        if ($memory_locations->has($pointer->address)) {
514
            $memory_location = $memory_locations->get($pointer->address);
515
            if ($memory_location instanceof ZendResourceMemoryLocation) {
516
                return $context_pools
517
                    ->resource_context_pool
518
                    ->getContextForLocation($memory_location)
519
                ;
520
            }
521
        }
522
        $resource = $dereferencer->deref($pointer);
523
        $memory_location = ZendResourceMemoryLocation::fromZendReference($resource);
524
        $memory_locations->add($memory_location);
525
        return $context_pools
526
            ->resource_context_pool
527
            ->getContextForLocation($memory_location)
528
        ;
529
    }
530
531
532
    /** @param Pointer<ZendReference> $pointer */
533
    public function collectPhpReferencePointer(
534
        Pointer $pointer,
535
        int $map_ptr_base,
536
        MemoryLocations $memory_locations,
537
        Dereferencer $dereferencer,
538
        ZendTypeReader $zend_type_reader,
539
        ContextPools $context_pools
540
    ): PhpReferenceContext {
541
        if ($memory_locations->has($pointer->address)) {
542
            $memory_location = $memory_locations->get($pointer->address);
543
            if ($memory_location instanceof ZendReferenceMemoryLocation) {
544
                return $context_pools
545
                    ->php_reference_context_pool
546
                    ->getContextForLocation($memory_location)
547
                ;
548
            }
549
        }
550
        $php_reference = $dereferencer->deref($pointer);
551
        $memory_location = ZendReferenceMemoryLocation::fromZendReference($php_reference);
552
        $memory_locations->add($memory_location);
553
        $php_referencecontext = $context_pools
554
            ->php_reference_context_pool
555
            ->getContextForLocation($memory_location)
556
        ;
557
        $zval_context = $this->collectZval(
558
            $php_reference->val,
559
            $map_ptr_base,
560
            $dereferencer,
561
            $zend_type_reader,
562
            $memory_locations,
563
            $context_pools,
564
        );
565
        if (!is_null($zval_context)) {
566
            $php_referencecontext->add('referenced', $zval_context);
567
        }
568
        return $php_referencecontext;
569
    }
570
571
    /** @param Pointer<ZendArray> $pointer */
572
    public function collectZendArrayPointer(
573
        Pointer $pointer,
574
        int $map_ptr_base,
575
        MemoryLocations $memory_locations,
576
        Dereferencer $dereferencer,
577
        ZendTypeReader $zend_type_reader,
578
        ContextPools $context_pools
579
    ): ArrayHeaderContext {
580
        if ($memory_locations->has($pointer->address)) {
581
            $memory_location = $memory_locations->get($pointer->address);
582
            if ($memory_location instanceof ZendArrayMemoryLocation) {
583
                return $context_pools
584
                    ->array_context_pool
585
                    ->getContextForLocation($memory_location)
586
                ;
587
            }
588
        }
589
        $array = $dereferencer->deref($pointer);
590
        return $this->collectZendArray(
591
            $array,
592
            $map_ptr_base,
593
            $dereferencer,
594
            $zend_type_reader,
595
            $memory_locations,
596
            $context_pools,
597
        );
598
    }
599
600
    /** @param Pointer<ZendObject> $pointer */
601
    public function collectZendObjectPointer(
602
        Pointer $pointer,
603
        int $map_ptr_base,
604
        MemoryLocations $memory_locations,
605
        Dereferencer $dereferencer,
606
        ZendTypeReader $zend_type_reader,
607
        ContextPools $context_pools
608
    ): ObjectContext {
609
        if ($memory_locations->has($pointer->address)) {
610
            $memory_location = $memory_locations->get($pointer->address);
611
            if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
612
                unset($memory_location);
613
            } else {
614
                assert($memory_location instanceof ZendObjectMemoryLocation);
615
                return $context_pools
616
                    ->object_context_pool
617
                    ->getContextForLocation($memory_location)
618
                    ;
619
            }
620
        }
621
        $obj = $dereferencer->deref($pointer);
622
        return $this->collectZendObject(
623
            $obj,
624
            $map_ptr_base,
625
            $dereferencer,
626
            $zend_type_reader,
627
            $memory_locations,
628
            $context_pools,
629
        );
630
    }
631
632
    /** @param Pointer<ZendString> $pointer */
633
    public function collectZendStringPointer(
634
        Pointer $pointer,
635
        MemoryLocations $memory_locations,
636
        Dereferencer $dereferencer,
637
        ContextPools $context_pools
638
    ): StringContext {
639
        if ($memory_locations->has($pointer->address)) {
640
            $memory_location = $memory_locations->get($pointer->address);
641
            if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
642
                $memory_location = null;
643
            } else {
644
                assert($memory_location instanceof ZendStringMemoryLocation);
645
            }
646
        }
647
        if (!isset($memory_location)) {
648
            $str = $dereferencer->deref($pointer);
649
            $memory_location = ZendStringMemoryLocation::fromZendString(
650
                $str,
651
                $dereferencer,
652
            );
653
            $memory_locations->add($memory_location);
654
        }
655
        assert($memory_location instanceof ZendStringMemoryLocation);
656
        return $context_pools
657
            ->string_context_pool
658
            ->getContextForLocation($memory_location)
659
        ;
660
    }
661
662
    public function collectCallFrames(
663
        ZendExecutorGlobals $eg,
664
        int $map_ptr_base,
665
        Dereferencer $dereferencer,
666
        ZendTypeReader $zend_type_reader,
667
        MemoryLocations $memory_locations,
668
        ContextPools $context_pools,
669
    ): CallFramesContext {
670
        $call_frames_context = new CallFramesContext();
671
        if (is_null($eg->current_execute_data)) {
672
            return $call_frames_context;
673
        }
674
        $execute_data = $dereferencer->deref($eg->current_execute_data);
675
        foreach ($execute_data->iterateStackChain($dereferencer) as $key => $execute_data) {
676
            $call_frame_context = $this->collectCallFrame(
677
                $execute_data,
678
                $map_ptr_base,
679
                $dereferencer,
680
                $zend_type_reader,
681
                $memory_locations,
682
                $context_pools,
683
            );
684
            $call_frames_context->add((string)$key, $call_frame_context);
685
        }
686
        return $call_frames_context;
687
    }
688
689
    public function collectRealCallStackOnMemoryLimitViolation(
690
        UserFunctionDefinitionContext $memory_limit_error_function_context,
691
        int $max_challenge_depth,
692
        CallFramesContext $call_frames_context,
693
        ZendExecutorGlobals $eg,
694
        int $map_ptr_base,
695
        Dereferencer $dereferencer,
696
        ZendTypeReader $zend_type_reader,
697
        MemoryLocations $memory_locations,
698
        ContextPools $context_pools,
699
    ): CallFramesContext {
700
        $op_array_address = $memory_limit_error_function_context->getOpArrayAddress();
701
702
        if (is_null($eg->vm_stack)) {
703
            return $call_frames_context;
704
        }
705
        if (is_null($eg->vm_stack_top)) {
706
            return $call_frames_context;
707
        }
708
709
        $last_vm_stack = $dereferencer->deref($eg->vm_stack);
710
        $root_vm_stack = $last_vm_stack->getRootStack($dereferencer);
711
        if (is_null($root_vm_stack->top)) {
712
            return $call_frames_context;
713
        }
714
715
        $first_stack = true;
716
        foreach ($last_vm_stack->iterateStackChain($dereferencer) as $vm_stack) {
717
            if ($first_stack) {
718
                $first_stack = false;
719
                $stack_end_address = $eg->vm_stack_top->address;
720
            } else {
721
                if (is_null($vm_stack->end)) {
722
                    break;
723
                }
724
                $stack_end_address = $vm_stack->end->address;
725
            }
726
            $materialized_vm_stack = $vm_stack->materializeAsPointerArray(
727
                $dereferencer,
728
                $stack_end_address
729
            );
730
            foreach ($materialized_vm_stack->getReverseIteratorAsInt() as $key => $value) {
731
                if ($value !== $op_array_address) {
732
                    continue;
733
                }
734
                $pointer_address = $key * 8 + $materialized_vm_stack->getPointer()->address - 24;
735
                Log::debug('candidate frame found', ['frame_address' => $pointer_address]);
736
                $frame_candidate = new Pointer(
737
                    ZendExecuteData::class,
738
                    $pointer_address,
739
                    $zend_type_reader->sizeOf('zend_execute_data')
740
                );
741
                try {
742
                    $execute_data_candidate = $dereferencer->deref($frame_candidate);
743
                    $root_execute_data_candidate = $execute_data_candidate->getRootFrame(
744
                        $dereferencer,
745
                        $max_challenge_depth,
746
                    );
747
                    if ($root_vm_stack->top->address !== $root_execute_data_candidate->getPointer()->address) {
748
                        continue;
749
                    }
750
                    Log::debug('root candidate frame found', ['frame_address' => $root_vm_stack->top->address]);
751
                    $frame_start = count($call_frames_context->getLinks());
752
                    foreach ($execute_data_candidate->iterateStackChain($dereferencer) as $frame_no => $execute_data) {
753
                        $call_frame_context = $this->collectCallFrame(
754
                            $execute_data,
755
                            $map_ptr_base,
756
                            $dereferencer,
757
                            $zend_type_reader,
758
                            $memory_locations,
759
                            $context_pools,
760
                        );
761
                        $call_frames_context->add((string)($frame_no + $frame_start), $call_frame_context);
762
                    }
763
                    return $call_frames_context;
764
                } catch (\Throwable $e) {
765
                    Log::debug(
766
                        'failed to collect real call stack from this candidate',
767
                        [
768
                            'exception' => $e,
769
                            'frame_address' => $pointer_address
770
                        ]
771
                    );
772
                    continue;
773
                }
774
            }
775
        }
776
        return $call_frames_context;
777
    }
778
779
    public function collectCallFrame(
780
        ZendExecuteData $execute_data,
781
        int $map_ptr_base,
782
        Dereferencer $dereferencer,
783
        ZendTypeReader $zend_type_reader,
784
        MemoryLocations $memory_locations,
785
        ContextPools $context_pools,
786
    ): CallFrameContext {
787
        $function_name = $execute_data->getFullyQualifiedFunctionName(
788
            $dereferencer,
789
            $zend_type_reader,
790
        );
791
792
        $lineno = -1;
793
        if ($execute_data->opline !== null and !$execute_data->isInternalCall($dereferencer)) {
794
            $opline = $dereferencer->deref($execute_data->opline);
795
            $lineno = $opline->lineno;
796
        }
797
798
        $header_memory_location = CallFrameHeaderMemoryLocation::fromZendExecuteData(
799
            $execute_data,
800
        );
801
        $call_frame_context = new CallFrameContext(
802
            $function_name,
803
            $lineno,
804
        );
805
        $variable_table_memory_location = CallFrameVariableTableMemoryLocation::fromZendExecuteData(
806
            $execute_data,
807
            $dereferencer
808
        );
809
        $memory_locations->add($variable_table_memory_location);
810
        $memory_locations->add($header_memory_location);
811
812
        if ($execute_data->hasThis()) {
813
            $this_context = $this->collectZval(
814
                $execute_data->This,
815
                $map_ptr_base,
816
                $dereferencer,
817
                $zend_type_reader,
818
                $memory_locations,
819
                $context_pools,
820
            );
821
            if (!is_null($this_context)) {
822
                $call_frame_context->add('this', $this_context);
823
            }
824
        }
825
826
        $has_local_variables = false;
827
        $variable_table_context = new CallFrameVariableTableContext();
828
        $local_variables_iterator = $execute_data->getVariables($dereferencer, $zend_type_reader);
829
        foreach ($local_variables_iterator as $name => $value) {
830
            $local_variable_context = $this->collectZval(
831
                $value,
832
                $map_ptr_base,
833
                $dereferencer,
834
                $zend_type_reader,
835
                $memory_locations,
836
                $context_pools,
837
            );
838
            if (!is_null($local_variable_context)) {
839
                $variable_table_context->add($name, $local_variable_context);
840
                $has_local_variables = true;
841
            }
842
        }
843
        if ($has_local_variables) {
844
            $call_frame_context->add('local_variables', $variable_table_context);
845
        }
846
847
        if ($execute_data->hasSymbolTable() and !is_null($execute_data->symbol_table)) {
848
            $symbol_table_context = $this->collectZendArrayPointer(
849
                $execute_data->symbol_table,
850
                $map_ptr_base,
851
                $memory_locations,
852
                $dereferencer,
853
                $zend_type_reader,
854
                $context_pools,
855
            );
856
            $call_frame_context->add('symbol_table', $symbol_table_context);
857
        }
858
        if ($execute_data->hasExtraNamedParams() and !is_null($execute_data->extra_named_params)) {
859
            $extra_named_params_context = $this->collectZendArrayPointer(
860
                $execute_data->extra_named_params,
861
                $map_ptr_base,
862
                $memory_locations,
863
                $dereferencer,
864
                $zend_type_reader,
865
                $context_pools,
866
            );
867
            $call_frame_context->add('extra_named_params', $extra_named_params_context);
868
        }
869
        return $call_frame_context;
870
    }
871
872
    public function collectGlobalVariables(
873
        ZendArray $array,
874
        int $map_ptr_base,
875
        Dereferencer $dereferencer,
876
        ZendTypeReader $zend_type_reader,
877
        MemoryLocations $memory_locations,
878
        ContextPools $context_pools
879
    ): GlobalVariablesContext {
880
        return GlobalVariablesContext::fromArrayContext(
881
            $this->collectZendArray(
882
                $array,
883
                $map_ptr_base,
884
                $dereferencer,
885
                $zend_type_reader,
886
                $memory_locations,
887
                $context_pools,
888
            )
889
        );
890
    }
891
892
    public function collectZendArray(
893
        ZendArray $array,
894
        int $map_ptr_base,
895
        Dereferencer $dereferencer,
896
        ZendTypeReader $zend_type_reader,
897
        MemoryLocations $memory_locations,
898
        ContextPools $context_pools
899
    ): ArrayHeaderContext {
900
        $array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
901
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
902
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
903
            $array,
904
            $array_table_location
905
        );
906
907
        $memory_locations->add($array_header_location);
908
        $memory_locations->add($array_table_location);
909
        $memory_locations->add($array_table_overhead_location);
910
911
        $array_header_context = $context_pools
912
            ->array_context_pool
913
            ->getContextForLocation($array_header_location)
914
        ;
915
        $array_context = new ArrayElementsContext($array_table_location);
916
        $overhead_context = new ArrayPossibleOverheadContext($array_table_overhead_location);
917
918
        foreach ($array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer) as $key => $zval) {
919
            $element_context = new ArrayElementContext();
920
            if ($key instanceof Pointer) {
921
                $key_context = $this->collectZendStringPointer(
922
                    $key,
923
                    $memory_locations,
924
                    $dereferencer,
925
                    $context_pools,
926
                );
927
                $zend_string = $dereferencer->deref($key);
928
                $key_string = $zend_string->toString($dereferencer);
929
                $element_context->add('key', $key_context);
930
            } else {
931
                $key_string = (string)$key;
932
            }
933
            $array_context->add($key_string, $element_context);
934
            $value_context = $this->collectZval(
935
                $zval,
936
                $map_ptr_base,
937
                $dereferencer,
938
                $zend_type_reader,
939
                $memory_locations,
940
                $context_pools,
941
            );
942
            if (!is_null($value_context)) {
943
                $element_context->add('value', $value_context);
944
            }
945
        }
946
        $array_header_context->add('possible_unused_area', $overhead_context);
947
        $array_header_context->add('array_elements', $array_context);
948
        return $array_header_context;
949
    }
950
951
    public function collectZendObject(
952
        ZendObject $object,
953
        int $map_ptr_base,
954
        Dereferencer $dereferencer,
955
        ZendTypeReader $zend_type_reader,
956
        MemoryLocations $memory_locations,
957
        ContextPools $context_pools
958
    ): ObjectContext {
959
        $object_location = ZendObjectMemoryLocation::fromZendObject(
960
            $object,
961
            $dereferencer,
962
            $zend_type_reader,
963
        );
964
        $object_handlers_memory_location = ZendObjectHandlersMemoryLocation::fromZendObject(
965
            $object,
966
            $zend_type_reader,
967
        );
968
        $memory_locations->add($object_location);
969
        $memory_locations->add($object_handlers_memory_location);
970
971
        $object_context = $context_pools
972
            ->object_context_pool->getContextForLocation(
973
                $object_location,
974
            )
975
        ;
976
        $object_handlers_context = $context_pools
977
            ->object_context_pool
978
            ->getHandlersContextForLocation(
979
                $object_handlers_memory_location,
980
            )
981
        ;
982
        $object_context->add('object_handlers', $object_handlers_context);
983
984
        $properties_exists = false;
985
        $object_properties_context = new ObjectPropertiesContext();
986
        $properties_iterator = $object->getPropertiesIterator(
987
            $dereferencer,
988
            $zend_type_reader,
989
        );
990
        foreach ($properties_iterator as $name => $property) {
991
            assert(is_string($name));
992
            $property_context = $this->collectZval(
993
                $property,
994
                $map_ptr_base,
995
                $dereferencer,
996
                $zend_type_reader,
997
                $memory_locations,
998
                $context_pools,
999
            );
1000
            if (!is_null($property_context)) {
1001
                $object_properties_context->add($name, $property_context);
1002
                $properties_exists = true;
1003
            }
1004
        }
1005
        if ($properties_exists) {
1006
            $object_context->add('object_properties', $object_properties_context);
1007
        }
1008
1009
        if (
1010
            !is_null($object->properties)
1011
            and !is_null($object->ce)
1012
            and !$object->isEnum($dereferencer)
1013
        ) {
1014
            $dynamic_properties_context = $this->collectZendArray(
1015
                $dereferencer->deref($object->properties),
1016
                $map_ptr_base,
1017
                $dereferencer,
1018
                $zend_type_reader,
1019
                $memory_locations,
1020
                $context_pools,
1021
            );
1022
            $object_context->add('dynamic_properties', $dynamic_properties_context);
1023
        }
1024
1025
        assert(!is_null($object->ce));
1026
        $class_entry = $dereferencer->deref($object->ce);
1027
        if ($class_entry->getClassName($dereferencer) === 'Closure') {
1028
            $closure_context = $this->collectClosure(
1029
                $dereferencer->deref(
1030
                    ZendClosure::getPointerFromZendObjectPointer(
1031
                        $object->getPointer(),
1032
                        $zend_type_reader,
1033
                    ),
1034
                ),
1035
                $map_ptr_base,
1036
                $dereferencer,
1037
                $zend_type_reader,
1038
                $memory_locations,
1039
                $context_pools,
1040
            );
1041
            $object_context->add('closure', $closure_context);
1042
        }
1043
1044
        return $object_context;
1045
    }
1046
1047
    public function collectClosure(
1048
        ZendClosure $zend_closure,
1049
        int $map_ptr_base,
1050
        Dereferencer $dereferencer,
1051
        ZendTypeReader $zend_type_reader,
1052
        MemoryLocations $memory_locations,
1053
        ContextPools $context_pools,
1054
    ): ClosureContext {
1055
        $closure_context = new ClosureContext();
1056
        $closure_context->add(
1057
            'func',
1058
            $this->collectZendFunctionPointer(
1059
                $zend_closure->func->getPointer(),
1060
                $map_ptr_base,
1061
                $dereferencer,
1062
                $zend_type_reader,
1063
                $memory_locations,
1064
                $context_pools,
1065
            )
1066
        );
1067
        $zval_context = $this->collectZval(
1068
            $zend_closure->this_ptr,
1069
            $map_ptr_base,
1070
            $dereferencer,
1071
            $zend_type_reader,
1072
            $memory_locations,
1073
            $context_pools,
1074
        );
1075
        if (!is_null($zval_context)) {
1076
            $closure_context->add(
1077
                'this_ptr',
1078
                $zval_context,
1079
            );
1080
        }
1081
        return $closure_context;
1082
    }
1083
1084
    public function collectFunctionTable(
1085
        ZendArray $array,
1086
        int $map_ptr_base,
1087
        Dereferencer $dereferencer,
1088
        ZendTypeReader $zend_type_reader,
1089
        MemoryLocations $memory_locations,
1090
        ContextPools $context_pools,
1091
        MemoryLimitErrorDetails $memory_limit_error_details = null,
1092
    ): DefinedFunctionsContext {
1093
        $array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
1094
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
1095
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
1096
            $array,
1097
            $array_table_location
1098
        );
1099
1100
        $memory_locations->add($array_header_location);
1101
        $memory_locations->add($array_table_location);
1102
        $memory_locations->add($array_table_overhead_location);
1103
1104
        $defined_functions_context = new DefinedFunctionsContext(
1105
            $array_header_location,
1106
            $array_table_location,
1107
        );
1108
1109
        foreach ($array->getItemIterator($dereferencer) as $function_name => $zval) {
1110
            assert(is_string($function_name));
1111
            assert(!is_null($zval->value->func));
1112
            $function_context = $this->collectZendFunctionPointer(
1113
                $zval->value->func,
1114
                $map_ptr_base,
1115
                $dereferencer,
1116
                $zend_type_reader,
1117
                $memory_locations,
1118
                $context_pools,
1119
                $memory_limit_error_details,
1120
            );
1121
            $defined_functions_context->add($function_name, $function_context);
1122
        }
1123
        return $defined_functions_context;
1124
    }
1125
1126
    /** @param Pointer<ZendFunction> $pointer */
1127
    public function collectZendFunctionPointer(
1128
        Pointer $pointer,
1129
        int $map_ptr_base,
1130
        Dereferencer $dereferencer,
1131
        ZendTypeReader $zend_type_reader,
1132
        MemoryLocations $memory_locations,
1133
        ContextPools $context_pools,
1134
        MemoryLimitErrorDetails $memory_limit_error_details = null,
1135
    ): FunctionDefinitionContext {
1136
        if ($memory_locations->has($pointer->address)) {
1137
            $memory_location = $memory_locations->get($pointer->address);
1138
            if ($memory_location instanceof ZendOpArrayHeaderMemoryLocation) {
1139
                return $context_pools
1140
                    ->user_function_definition_context_pool
1141
                    ->getContextForLocation($memory_location)
1142
                ;
1143
            }
1144
        }
1145
        $func = $dereferencer->deref($pointer);
1146
        return $this->collectZendFunction(
1147
            $func,
1148
            $map_ptr_base,
1149
            $dereferencer,
1150
            $zend_type_reader,
1151
            $memory_locations,
1152
            $context_pools,
1153
            $memory_limit_error_details,
1154
        );
1155
    }
1156
1157
    public function collectZendFunction(
1158
        ZendFunction $func,
1159
        int $map_ptr_base,
1160
        Dereferencer $dereferencer,
1161
        ZendTypeReader $zend_type_reader,
1162
        MemoryLocations $memory_locations,
1163
        ContextPools $context_pools,
1164
        MemoryLimitErrorDetails $memory_limit_error_details = null,
1165
    ): FunctionDefinitionContext {
1166
        if ($func->isUserFunction()) {
1167
            $function_definition_context = $this->collectUserFunctionDefinition(
1168
                $func,
1169
                $map_ptr_base,
1170
                $dereferencer,
1171
                $zend_type_reader,
1172
                $memory_locations,
1173
                $context_pools,
1174
                $memory_limit_error_details,
1175
            );
1176
        } else {
1177
            $function_definition_context = new InternalFunctionDefinitionContext();
1178
        }
1179
        if (!is_null($func->function_name)) {
1180
            $function_name_context = $this->collectZendStringPointer(
1181
                $func->function_name,
1182
                $memory_locations,
1183
                $dereferencer,
1184
                $context_pools,
1185
            );
1186
            $function_definition_context->add('name', $function_name_context);
1187
        }
1188
        return $function_definition_context;
1189
    }
1190
1191
    public function collectUserFunctionDefinition(
1192
        ZendFunction $func,
1193
        int $map_ptr_base,
1194
        Dereferencer $dereferencer,
1195
        ZendTypeReader $zend_type_reader,
1196
        MemoryLocations $memory_locations,
1197
        ContextPools $context_pools,
1198
        MemoryLimitErrorDetails $memory_limit_error_details = null,
1199
    ): UserFunctionDefinitionContext {
1200
        $function_name = $func->getFullyQualifiedFunctionName(
1201
            $dereferencer,
1202
            $zend_type_reader,
1203
        );
1204
        $op_array_header_memory_location = ZendOpArrayHeaderMemoryLocation::fromZendFunction(
1205
            $func,
1206
            $zend_type_reader,
1207
            $dereferencer,
1208
        );
1209
        $op_array_body_memory_location = ZendOpArrayBodyMemoryLocation::fromZendFunction(
1210
            $func,
1211
            $zend_type_reader,
1212
            $function_name
1213
        );
1214
        $memory_locations->add($op_array_header_memory_location);
1215
        $memory_locations->add($op_array_body_memory_location);
1216
        $function_definition_context = $context_pools
1217
            ->user_function_definition_context_pool
1218
            ->getContextForLocation($op_array_header_memory_location)
1219
        ;
1220
        $op_array_context = new OpArrayContext(
1221
            $op_array_header_memory_location,
1222
            $op_array_body_memory_location,
1223
        );
1224
        $function_definition_context->add('op_array', $op_array_context);
1225
1226
        if ($func->op_array->cache_size > 0) {
1227
            $runtime_cache_memory_location = RuntimeCacheMemoryLocation::fromZendOpArray(
1228
                $func->op_array,
1229
                $dereferencer,
1230
                $zend_type_reader,
1231
                $map_ptr_base,
1232
            );
1233
            if ($runtime_cache_memory_location->address !== 0) {
1234
                $memory_locations->add($runtime_cache_memory_location);
1235
                $run_time_cache_context = new RuntimeCacheContext($runtime_cache_memory_location);
1236
                $op_array_context->add('run_time_cache', $run_time_cache_context);
1237
            }
1238
        }
1239
1240
        if (!is_null($func->op_array->arg_info)) {
1241
            $arginfos_memory_location = ZendArgInfosMemoryLocation::fromZendOpArray(
1242
                $func->op_array,
1243
                $zend_type_reader,
1244
            );
1245
            $memory_locations->add($arginfos_memory_location);
1246
            $arginfos_context = new ArgInfosContext($arginfos_memory_location);
1247
            $op_array_context->add('arg_infos', $arginfos_context);
1248
            foreach ($func->op_array->iterateArgInfo($dereferencer, $zend_type_reader) as $arg_info) {
1249
                if (!is_null($arg_info->name)) {
1250
                    $arg_info_context = new ArgInfoContext();
1251
                    $arg_info_name_context = $this->collectZendStringPointer(
1252
                        $arg_info->name,
1253
                        $memory_locations,
1254
                        $dereferencer,
1255
                        $context_pools,
1256
                    );
1257
                    $arg_info_name = $dereferencer
1258
                        ->deref($arg_info->name)
1259
                        ->toString($dereferencer)
1260
                    ;
1261
                    $arg_info_context->add('name', $arg_info_name_context);
1262
                    $arginfos_context->add($arg_info_name, $arg_info_context);
1263
                }
1264
            }
1265
        }
1266
1267
        if (!is_null($func->op_array->doc_comment)) {
1268
            $doc_comment_context = $this->collectZendStringPointer(
1269
                $func->op_array->doc_comment,
1270
                $memory_locations,
1271
                $dereferencer,
1272
                $context_pools,
1273
            );
1274
            $op_array_context->add('doc_comment', $doc_comment_context);
1275
        }
1276
1277
        if (!is_null($func->op_array->filename)) {
1278
            $file_name_context = $this->collectZendStringPointer(
1279
                $func->op_array->filename,
1280
                $memory_locations,
1281
                $dereferencer,
1282
                $context_pools,
1283
            );
1284
            $op_array_context->add('filename', $file_name_context);
1285
        }
1286
1287
        if (!is_null($func->op_array->static_variables)) {
1288
            $static_variables_context = $this->collectZendArray(
1289
                $dereferencer->deref($func->op_array->static_variables),
1290
                $map_ptr_base,
1291
                $dereferencer,
1292
                $zend_type_reader,
1293
                $memory_locations,
1294
                $context_pools,
1295
            );
1296
            $op_array_context->add('static_variables', $static_variables_context);
1297
        }
1298
1299
        if (!is_null($func->op_array->vars)) {
1300
            $local_variable_name_table_location = LocalVariableNameTableMemoryLocation::fromZendOpArray(
1301
                $func->op_array
1302
            );
1303
            $memory_locations->add($local_variable_name_table_location);
1304
            $variable_name_table_context = new LocalVariableNameTableContext(
1305
                $local_variable_name_table_location
1306
            );
1307
            $op_array_context->add('variable_name_table', $variable_name_table_context);
1308
1309
            $variable_names_iterator = $func->op_array->getVariableNamesAsIteratorOfPointersToZendStrings(
1310
                $dereferencer,
1311
                $zend_type_reader,
1312
            );
1313
            foreach ($variable_names_iterator as $key => $variable_name) {
1314
                $variable_name_context = $this->collectZendStringPointer(
1315
                    $variable_name,
1316
                    $memory_locations,
1317
                    $dereferencer,
1318
                    $context_pools,
1319
                );
1320
                $variable_name_table_context->add((string)$key, $variable_name_context);
1321
            }
1322
        }
1323
1324
        if ($func->op_array->num_dynamic_func_defs > 0) {
1325
            $dynamic_func_defs_table_memory_location = DynamicFuncDefsTableMemoryLocation::fromZendOpArray(
1326
                $func->op_array,
1327
            );
1328
            $memory_locations->add($dynamic_func_defs_table_memory_location);
1329
            $dynamic_func_defs_context = new DynamicFuncDefsContext(
1330
                $dynamic_func_defs_table_memory_location
1331
            );
1332
            $dynamic_func_defs_iterator = $func->op_array->iterateDynamicFunctionDefinitions(
1333
                $dereferencer,
1334
                $zend_type_reader,
1335
            );
1336
            foreach ($dynamic_func_defs_iterator as $key => $dynamic_func_def) {
1337
                $dynamic_function_context = $this->collectZendFunctionPointer(
1338
                    $dynamic_func_def,
1339
                    $map_ptr_base,
1340
                    $dereferencer,
1341
                    $zend_type_reader,
1342
                    $memory_locations,
1343
                    $context_pools,
1344
                    $memory_limit_error_details,
1345
                );
1346
                $dynamic_func_defs_context->add((string)$key, $dynamic_function_context);
1347
            }
1348
            $op_array_context->add('dynamic_function_definitions', $dynamic_func_defs_context);
1349
        }
1350
1351
        if (!is_null($memory_limit_error_details)) {
1352
            if (
1353
                $function_definition_context->isThisContext(
1354
                    $memory_limit_error_details->file,
1355
                    $memory_limit_error_details->line,
1356
                )
1357
            ) {
1358
                if (
1359
                    is_null($this->memory_limit_error_function_context)
1360
                    or $function_definition_context->isClosureOf($this->memory_limit_error_function_context)
1361
                ) {
1362
                    $this->memory_limit_error_function_context = $function_definition_context;
1363
                }
1364
            }
1365
        }
1366
        return $function_definition_context;
1367
    }
1368
1369
    public function collectClassConstantsTable(
1370
        ZendArray $array,
1371
        int $map_ptr_base,
1372
        Dereferencer $dereferencer,
1373
        ZendTypeReader $zend_type_reader,
1374
        MemoryLocations $memory_locations,
1375
        ContextPools $context_pools
1376
    ): ClassConstantsContext {
1377
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
1378
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
1379
            $array,
1380
            $array_table_location
1381
        );
1382
        $memory_locations->add($array_table_location);
1383
        $memory_locations->add($array_table_overhead_location);
1384
        $class_constants_context = new ClassConstantsContext($array_table_location);
1385
1386
        $array_iterator = $array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
1387
        foreach ($array_iterator as $constant_name => $zval_or_ptr) {
1388
            assert($constant_name instanceof Pointer);
1389
            $constant_name_context = $this->collectZendStringPointer(
1390
                $constant_name,
1391
                $memory_locations,
1392
                $dereferencer,
1393
                $context_pools,
1394
            );
1395
            $zend_string = $dereferencer->deref($constant_name);
1396
            $constant_name_string = $zend_string->toString($dereferencer);
1397
            $constant_context = new ClassConstantContext();
1398
            $class_constants_context->add($constant_name_string, $constant_context);
1399
            $constant_context->add('name', $constant_name_context);
1400
1401
            if ($zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)) {
1402
                $zval = $zval_or_ptr;
1403
            } else {
1404
                $class_constant_ptr = $zval_or_ptr->value->getAsPointer(
1405
                    ZendClassConstant::class,
1406
                    $zend_type_reader->sizeOf(ZendClassConstant::getCTypeName()),
1407
                );
1408
                $class_constant = $dereferencer->deref(
1409
                    $class_constant_ptr
1410
                );
1411
                $memory_location = ZendClassConstantMemoryLocation::fromZendClassConstant(
1412
                    $class_constant,
1413
                );
1414
                $memory_locations->add($memory_location);
1415
                $zval = $class_constant->value;
1416
                $info_context = new ClassConstantInfoContext($memory_location);
1417
                $constant_context->add('info', $info_context);
1418
            }
1419
            $value_context = $this->collectZval(
1420
                $zval,
1421
                $map_ptr_base,
1422
                $dereferencer,
1423
                $zend_type_reader,
1424
                $memory_locations,
1425
                $context_pools,
1426
            );
1427
            if (!is_null($value_context)) {
1428
                $constant_context->add('value', $value_context);
1429
            }
1430
        }
1431
1432
        return $class_constants_context;
1433
    }
1434
1435
    public function collectClassTable(
1436
        ZendArray $array,
1437
        int $map_ptr_base,
1438
        Dereferencer $dereferencer,
1439
        ZendTypeReader $zend_type_reader,
1440
        MemoryLocations $memory_locations,
1441
        ContextPools $context_pools,
1442
        ?MemoryLimitErrorDetails $memory_limit_error_details = null,
1443
    ): DefinedClassesContext {
1444
        $defined_classes_context = new DefinedClassesContext();
1445
        foreach ($array->getItemIterator($dereferencer) as $class_name => $zval) {
1446
            assert(!is_null($zval->value->ce));
1447
            $class_definition_context = $this->collectClassDefinitionPointer(
1448
                $zval->value->ce,
1449
                $map_ptr_base,
1450
                $dereferencer,
1451
                $zend_type_reader,
1452
                $memory_locations,
1453
                $context_pools,
1454
                $memory_limit_error_details,
1455
            );
1456
            if (!is_null($class_definition_context)) {
1457
                $defined_classes_context->add((string)$class_name, $class_definition_context);
1458
            }
1459
        }
1460
        return $defined_classes_context;
1461
    }
1462
1463
    /** @param Pointer<ZendClassEntry> $pointer */
1464
    private function collectClassDefinitionPointer(
1465
        Pointer $pointer,
1466
        int $map_ptr_base,
1467
        Dereferencer $dereferencer,
1468
        ZendTypeReader $zend_type_reader,
1469
        MemoryLocations $memory_locations,
1470
        ContextPools $context_pools,
1471
        ?MemoryLimitErrorDetails $memory_limit_error_details = null,
1472
    ): ?ClassDefinitionContext {
1473
        if ($memory_locations->has($pointer->address)) {
1474
            return null;
1475
        }
1476
        $class_entry = $dereferencer->deref($pointer);
1477
        return $this->collectClassDefinition(
1478
            $class_entry,
1479
            $map_ptr_base,
1480
            $dereferencer,
1481
            $zend_type_reader,
1482
            $memory_locations,
1483
            $context_pools,
1484
            $memory_limit_error_details,
1485
        );
1486
    }
1487
1488
    private function collectClassDefinition(
1489
        ZendClassEntry $class_entry,
1490
        int $map_ptr_base,
1491
        Dereferencer $dereferencer,
1492
        ZendTypeReader $zend_type_reader,
1493
        MemoryLocations $memory_locations,
1494
        ContextPools $context_pools,
1495
        ?MemoryLimitErrorDetails $memory_limit_error_details = null,
1496
    ): ClassDefinitionContext {
1497
        $class_definition_context = new ClassDefinitionContext($class_entry->isInternal());
1498
        $memory_location = ZendClassEntryMemoryLocation::fromZendClassEntry($class_entry);
1499
        $memory_locations->add($memory_location);
1500
        $class_entry_context = new ClassEntryContext($memory_location);
1501
        $class_definition_context->add('class_entry', $class_entry_context);
1502
1503
        $class_name_context = $this->collectZendStringPointer(
1504
            $class_entry->name,
1505
            $memory_locations,
1506
            $dereferencer,
1507
            $context_pools,
1508
        );
1509
        $class_definition_context->add('name', $class_name_context);
1510
1511
        if (!$class_entry->isInternal()) {
1512
            if (!is_null($class_entry->info->user->filename)) {
1513
                $file_name_context = $this->collectZendStringPointer(
1514
                    $class_entry->info->user->filename,
1515
                    $memory_locations,
1516
                    $dereferencer,
1517
                    $context_pools,
1518
                );
1519
                $class_definition_context->add('filename', $file_name_context);
1520
            }
1521
1522
            if (!is_null($class_entry->info->user->doc_comment)) {
1523
                $doc_comment_context = $this->collectZendStringPointer(
1524
                    $class_entry->info->user->doc_comment,
1525
                    $memory_locations,
1526
                    $dereferencer,
1527
                    $context_pools,
1528
                );
1529
                $class_definition_context->add('doc_comment', $doc_comment_context);
1530
            }
1531
        }
1532
1533
        if (
1534
            $class_entry->default_static_members_count > 0
1535
            and !is_null($class_entry->static_members_table)
1536
        ) {
1537
            $static_members_table_memory_location = StaticMembersTableMemoryLocation::fromZendClassEntry(
1538
                $class_entry,
1539
                $zend_type_reader,
1540
                $dereferencer,
1541
                $map_ptr_base,
1542
            );
1543
            $memory_locations->add($static_members_table_memory_location);
1544
            $static_properties_context = new ClassStaticPropertiesContext(
1545
                $static_members_table_memory_location
1546
            );
1547
            $static_property_iterator = $class_entry->getStaticPropertyIterator(
1548
                $dereferencer,
1549
                $zend_type_reader,
1550
                $map_ptr_base,
1551
            );
1552
            foreach ($static_property_iterator as $name => $value) {
1553
                $static_property_context = $this->collectZval(
1554
                    $value,
1555
                    $map_ptr_base,
1556
                    $dereferencer,
1557
                    $zend_type_reader,
1558
                    $memory_locations,
1559
                    $context_pools,
1560
                );
1561
                if (!is_null($static_property_context)) {
1562
                    $static_properties_context->add($name, $static_property_context);
1563
                }
1564
            }
1565
            $class_definition_context->add('static_properties', $static_properties_context);
1566
        }
1567
1568
        $properties_info_context = $this->collectPropertiesInfo(
1569
            $class_entry,
1570
            $dereferencer,
1571
            $zend_type_reader,
1572
            $memory_locations,
1573
            $context_pools,
1574
        );
1575
        $class_definition_context->add('property_info', $properties_info_context);
1576
1577
        if (!is_null($class_entry->default_properties_table)) {
1578
            $default_properties_table_memory_location = DefaultPropertiesTableMemoryLocation::fromZendClassEntry(
1579
                $class_entry,
1580
            );
1581
            $memory_locations->add($default_properties_table_memory_location);
1582
            $default_properties_context = new DefaultPropertiesTableContext(
1583
                $default_properties_table_memory_location
1584
            );
1585
            $class_definition_context->add('default_properties', $default_properties_context);
1586
        }
1587
1588
        if (!is_null($class_entry->default_static_members_table)) {
1589
            $default_static_members_memory_location = DefaultStaticMembersTableMemoryLocation::fromZendClassEntry(
1590
                $class_entry,
1591
            );
1592
            $memory_locations->add($default_static_members_memory_location);
1593
            $default_static_properties_context = new DefaultStaticPropertiesContext(
1594
                $default_static_members_memory_location
1595
            );
1596
            $class_definition_context->add('default_static_properties', $default_static_properties_context);
1597
        }
1598
1599
        $methods_context = $this->collectFunctionTable(
1600
            $class_entry->function_table,
1601
            $map_ptr_base,
1602
            $dereferencer,
1603
            $zend_type_reader,
1604
            $memory_locations,
1605
            $context_pools,
1606
            $memory_limit_error_details,
1607
        );
1608
        $class_definition_context->add('methods', $methods_context);
1609
1610
        $class_constants_context = $this->collectClassConstantsTable(
1611
            $class_entry->constants_table,
1612
            $map_ptr_base,
1613
            $dereferencer,
1614
            $zend_type_reader,
1615
            $memory_locations,
1616
            $context_pools,
1617
        );
1618
        $class_definition_context->add('constants', $class_constants_context);
1619
1620
        return $class_definition_context;
1621
    }
1622
1623
    private function collectPropertiesInfo(
1624
        ZendClassEntry $class_entry,
1625
        Dereferencer $dereferencer,
1626
        ZendTypeReader $zend_type_reader,
1627
        MemoryLocations $memory_locations,
1628
        ContextPools $context_pools
1629
    ): PropertiesInfoContext {
1630
        $memory_location = ZendArrayTableMemoryLocation::fromZendArray($class_entry->properties_info);
1631
        $memory_locations->add($memory_location);
1632
1633
        $properties_info_context = new PropertiesInfoContext($memory_location);
1634
1635
        foreach ($class_entry->iteratePropertyInfo($dereferencer, $zend_type_reader) as $key => $property_info) {
1636
            $property_info_memory_location = ZendPropertyInfoMemoryLocation::fromZendPropertyInfo(
1637
                $property_info,
1638
            );
1639
            $memory_locations->add($property_info_memory_location);
1640
            $property_info_context = new PropertyInfoContext($property_info_memory_location);
1641
            if (!is_null($property_info->name)) {
1642
                $property_info_name_context = $this->collectZendStringPointer(
1643
                    $property_info->name,
1644
                    $memory_locations,
1645
                    $dereferencer,
1646
                    $context_pools,
1647
                );
1648
                $property_info_context->add('name', $property_info_name_context);
1649
            }
1650
            if (!is_null($property_info->doc_comment)) {
1651
                $property_info_doc_comment_context = $this->collectZendStringPointer(
1652
                    $property_info->doc_comment,
1653
                    $memory_locations,
1654
                    $dereferencer,
1655
                    $context_pools,
1656
                );
1657
                $property_info_context->add('doc_comment', $property_info_doc_comment_context);
1658
            }
1659
            $properties_info_context->add($key, $property_info_context);
1660
        }
1661
        return $properties_info_context;
1662
    }
1663
1664
    private function collectGlobalConstants(
1665
        ZendArray $array,
1666
        int $map_ptr_base,
1667
        Dereferencer $dereferencer,
1668
        ZendTypeReader $zend_type_reader,
1669
        MemoryLocations $memory_locations,
1670
        ContextPools $context_pools
1671
    ): GlobalConstantsContext {
1672
        $memory_location = ZendArrayTableMemoryLocation::fromZendArray($array);
1673
        $memory_locations->add($memory_location);
1674
1675
        $global_constants_context = new GlobalConstantsContext($memory_location);
1676
        foreach ($array->getItemIterator($dereferencer) as $constant_name => $zval) {
1677
            assert(is_string($constant_name));
1678
            $zend_constant = $dereferencer->deref(
1679
                $zval->value->getAsPointer(
1680
                    ZendConstant::class,
1681
                    $zend_type_reader->sizeOf(ZendConstant::getCTypeName()),
1682
                )
1683
            );
1684
            $zend_constant_memory_location = ZendConstantMemoryLocation::fromZendConstant(
1685
                $zend_constant
1686
            );
1687
            $constant_context = new GlobalConstantContext($zend_constant_memory_location);
1688
            $global_constants_context->add($constant_name, $constant_context);
1689
1690
            if (!is_null($zend_constant->name)) {
1691
                $constant_name_context = $this->collectZendStringPointer(
1692
                    $zend_constant->name,
1693
                    $memory_locations,
1694
                    $dereferencer,
1695
                    $context_pools,
1696
                );
1697
                $constant_context->add('name', $constant_name_context);
1698
            }
1699
1700
            $value_context = $this->collectZval(
1701
                $zend_constant->value,
1702
                $map_ptr_base,
1703
                $dereferencer,
1704
                $zend_type_reader,
1705
                $memory_locations,
1706
                $context_pools,
1707
            );
1708
            if (!is_null($value_context)) {
1709
                $constant_context->add('value', $value_context);
1710
            }
1711
        }
1712
        return $global_constants_context;
1713
    }
1714
1715
    private function collectIncludedFiles(
1716
        ZendArray $included_files,
1717
        Dereferencer $dereferencer,
1718
        MemoryLocations $memory_locations,
1719
        ContextPools $context_pools
1720
    ): IncludedFilesContext {
1721
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($included_files);
1722
        $included_files_context = new IncludedFilesContext($array_table_location);
1723
1724
        $memory_locations->add($array_table_location);
1725
1726
        $iterator = $included_files->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
1727
        foreach ($iterator as $filename => $_) {
1728
            assert($filename instanceof Pointer);
1729
            $raw_string = $dereferencer->deref($filename)->toString($dereferencer);
1730
            $included_file_context = $this->collectZendStringPointer(
1731
                $filename,
1732
                $memory_locations,
1733
                $dereferencer,
1734
                $context_pools,
1735
            );
1736
            $included_files_context->add($raw_string, $included_file_context);
1737
        }
1738
        return $included_files_context;
1739
    }
1740
1741
    private function collectInternedStrings(
1742
        ZendArray $interned_string,
1743
        int $map_ptr_base,
1744
        Dereferencer $dereferencer,
1745
        ZendTypeReader $zend_type_reader,
1746
        MemoryLocations $memory_locations,
1747
        ContextPools $context_pools
1748
    ): ArrayHeaderContext {
1749
        return $this->collectZendArray(
1750
            $interned_string,
1751
            $map_ptr_base,
1752
            $dereferencer,
1753
            $zend_type_reader,
1754
            $memory_locations,
1755
            $context_pools,
1756
        );
1757
    }
1758
1759
    private function collectObjectsStore(
1760
        ZendObjectsStore $objects_store,
1761
        int $map_ptr_base,
1762
        Dereferencer $dereferencer,
1763
        ZendTypeReader $zend_type_reader,
1764
        MemoryLocations $memory_locations,
1765
        ContextPools $context_pools
1766
    ): ObjectsStoreContext {
1767
        $objects_store_memory_location = ObjectsStoreMemoryLocation::fromZendObjectsStore(
1768
            $objects_store,
1769
        );
1770
        $objects_store_context = new ObjectsStoreContext($objects_store_memory_location);
1771
        $memory_locations->add($objects_store_memory_location);
1772
1773
        assert($objects_store->object_buckets instanceof Pointer);
1774
        $buckets = $dereferencer->deref($objects_store->object_buckets);
1775
        $bucket_iterator = $buckets->getIteratorOfPointersTo(
1776
            ZendObject::class,
1777
            $zend_type_reader,
1778
        );
1779
1780
        foreach ($bucket_iterator as $key => $bucket) {
1781
            if ($key === 0) {
1782
                continue;
1783
            }
1784
            if ($bucket->address & 1) {
1785
                continue;
1786
            }
1787
            if ($bucket->address === 0) {
1788
                continue;
1789
            }
1790
            if ($key >= $objects_store->top) {
1791
                break;
1792
            }
1793
            $objects_store_bucket_context = $this->collectZendObjectPointer(
1794
                $bucket,
1795
                $map_ptr_base,
1796
                $memory_locations,
1797
                $dereferencer,
1798
                $zend_type_reader,
1799
                $context_pools,
1800
            );
1801
            $objects_store_context->add((string)$key, $objects_store_bucket_context);
1802
        }
1803
        return $objects_store_context;
1804
    }
1805
}
1806