Passed
Pull Request — 0.9.x (#327)
by Shinji
01:51
created

php$0 ➔ collectZendObject()   C

Complexity

Conditions 8

Size

Total Lines 93

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 93
c 0
b 0
f 0
cc 8
rs 6.9082

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