modwire 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,816 @@
1
+ #!/usr/bin/env php
2
+ <?php
3
+
4
+ function line_count_for_content(string $content): int {
5
+ return max(1, substr_count($content, "\n") + 1);
6
+ }
7
+
8
+ function code_line_count_for_content(string $content): int {
9
+ $count = 0;
10
+ foreach (explode("\n", $content) as $line) {
11
+ $trimmed = trim($line);
12
+ if ($trimmed !== '' && !str_starts_with($trimmed, '//') && !str_starts_with($trimmed, '#') && !str_starts_with($trimmed, '/*') && !str_starts_with($trimmed, '*')) {
13
+ $count++;
14
+ }
15
+ }
16
+ return $count;
17
+ }
18
+
19
+ function token_line_value($token, int $fallbackLine): int {
20
+ return is_array($token) ? $token[2] : $fallbackLine;
21
+ }
22
+
23
+ function token_text($token): string {
24
+ return is_array($token) ? $token[1] : $token;
25
+ }
26
+
27
+ function normalize_namespace_reference(string $value): string {
28
+ return trim(str_replace('/', '\\', $value), " \\");
29
+ }
30
+
31
+ function normalize_import_path(string $value): string {
32
+ return trim(str_replace('\\', '/', $value), '/ ');
33
+ }
34
+
35
+ function import_parent_path(string $value): string {
36
+ $normalizedPath = normalize_import_path($value);
37
+ $parts = explode('/', $normalizedPath);
38
+ array_pop($parts);
39
+ return implode('/', $parts);
40
+ }
41
+
42
+ function is_name_token($token): bool {
43
+ if (!is_array($token)) {
44
+ return false;
45
+ }
46
+
47
+ $tokenId = $token[0];
48
+ $valid = [T_STRING];
49
+ if (defined('T_NS_SEPARATOR')) {
50
+ $valid[] = T_NS_SEPARATOR;
51
+ }
52
+ if (defined('T_NAME_QUALIFIED')) {
53
+ $valid[] = T_NAME_QUALIFIED;
54
+ }
55
+ if (defined('T_NAME_FULLY_QUALIFIED')) {
56
+ $valid[] = T_NAME_FULLY_QUALIFIED;
57
+ }
58
+ if (defined('T_NAME_RELATIVE')) {
59
+ $valid[] = T_NAME_RELATIVE;
60
+ }
61
+
62
+ return in_array($tokenId, $valid, true);
63
+ }
64
+
65
+ function use_import_entry(string $usePath, bool $isAliased, int $statementId, bool $usesJoinedImport): array {
66
+ $normalizedUsePath = normalize_namespace_reference($usePath);
67
+ return [
68
+ 'path' => $normalizedUsePath,
69
+ 'is_relative' => false,
70
+ 'normalized_path' => normalize_import_path($normalizedUsePath),
71
+ 'imported_name' => '',
72
+ 'is_aliased' => $isAliased,
73
+ 'crossing_type' => 'symbol',
74
+ 'file_barrier_crossed' => true,
75
+ 'statement_id' => $statementId,
76
+ 'join_key' => import_parent_path($normalizedUsePath),
77
+ 'uses_joined_import' => $usesJoinedImport,
78
+ ];
79
+ }
80
+
81
+ function use_statement_imports(array $tokens, int $useIndex, int $statementId): array {
82
+ $count = count($tokens);
83
+ $j = $useIndex + 1;
84
+ $prefix = '';
85
+ $imports = [];
86
+
87
+ while ($j < $count && token_text($tokens[$j]) !== ';') {
88
+ $token = $tokens[$j];
89
+ if ($token === '{') {
90
+ $j++;
91
+ $name = '';
92
+ $isAliased = false;
93
+ while ($j < $count && token_text($tokens[$j]) !== '}') {
94
+ $current = $tokens[$j];
95
+ if ($current === ',') {
96
+ if ($name !== '') {
97
+ $imports[] = use_import_entry(
98
+ rtrim($prefix, '\\') . '\\' . $name,
99
+ $isAliased,
100
+ $statementId,
101
+ true
102
+ );
103
+ }
104
+ $name = '';
105
+ $isAliased = false;
106
+ } elseif (is_array($current) && $current[0] === T_AS) {
107
+ $isAliased = true;
108
+ while ($j < $count && token_text($tokens[$j]) !== ',' && token_text($tokens[$j]) !== '}') {
109
+ $j++;
110
+ }
111
+ $j--;
112
+ } elseif (is_name_token($current)) {
113
+ $name .= token_text($current);
114
+ }
115
+ $j++;
116
+ }
117
+ if ($name !== '') {
118
+ $imports[] = use_import_entry(rtrim($prefix, '\\') . '\\' . $name, $isAliased, $statementId, true);
119
+ }
120
+ return $imports;
121
+ }
122
+ if ($token === ',') {
123
+ if ($prefix !== '') {
124
+ $imports[] = use_import_entry($prefix, false, $statementId, false);
125
+ }
126
+ $prefix = '';
127
+ } elseif (is_array($token) && $token[0] === T_AS) {
128
+ if ($prefix !== '') {
129
+ $imports[] = use_import_entry($prefix, true, $statementId, false);
130
+ }
131
+ while ($j < $count && token_text($tokens[$j]) !== ',' && token_text($tokens[$j]) !== ';') {
132
+ $j++;
133
+ }
134
+ $prefix = '';
135
+ continue;
136
+ } elseif (is_name_token($token)) {
137
+ $prefix .= token_text($token);
138
+ }
139
+ $j++;
140
+ }
141
+
142
+ if ($prefix !== '') {
143
+ $imports[] = use_import_entry($prefix, false, $statementId, false);
144
+ }
145
+
146
+ return $imports;
147
+ }
148
+
149
+ function member_visibility(array $tokens, int $functionIndex): string {
150
+ for ($index = $functionIndex - 1; $index >= 0; $index--) {
151
+ $token = $tokens[$index];
152
+ if (!is_array($token)) {
153
+ $text = token_text($token);
154
+ if ($text === ';' || $text === '{' || $text === '}') {
155
+ break;
156
+ }
157
+ continue;
158
+ }
159
+
160
+ $tokenId = $token[0];
161
+ if (in_array($tokenId, [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_STATIC, T_FINAL, T_ABSTRACT], true)) {
162
+ continue;
163
+ }
164
+ if ($tokenId === T_PRIVATE) {
165
+ return 'private';
166
+ }
167
+ if ($tokenId === T_PROTECTED) {
168
+ return 'protected';
169
+ }
170
+ if ($tokenId === T_PUBLIC) {
171
+ return 'public';
172
+ }
173
+ break;
174
+ }
175
+
176
+ return 'public';
177
+ }
178
+
179
+ function visibility_intent(string $name, string $visibility): string {
180
+ $magicMethods = [
181
+ '__call',
182
+ '__callStatic',
183
+ '__clone',
184
+ '__construct',
185
+ '__debugInfo',
186
+ '__destruct',
187
+ '__get',
188
+ '__invoke',
189
+ '__isset',
190
+ '__serialize',
191
+ '__set',
192
+ '__set_state',
193
+ '__sleep',
194
+ '__toString',
195
+ '__unserialize',
196
+ '__unset',
197
+ '__wakeup',
198
+ ];
199
+ if (in_array($name, $magicMethods, true)) {
200
+ return $visibility;
201
+ }
202
+ if (str_starts_with($name, '__')) {
203
+ return 'private';
204
+ }
205
+ if (str_starts_with($name, '_')) {
206
+ return 'protected';
207
+ }
208
+ return $visibility;
209
+ }
210
+
211
+ function find_block_end_line(array $tokens, int $startIndex, int $fallbackLine): int {
212
+ $depth = 0;
213
+ $seenBrace = false;
214
+ $currentLine = $fallbackLine;
215
+ $count = count($tokens);
216
+
217
+ for ($index = $startIndex; $index < $count; $index++) {
218
+ $token = $tokens[$index];
219
+ $currentLine = token_line_value($token, $currentLine);
220
+ $text = token_text($token);
221
+
222
+ if ($text === '{') {
223
+ $depth++;
224
+ $seenBrace = true;
225
+ continue;
226
+ }
227
+ if ($text === '}') {
228
+ if ($seenBrace) {
229
+ $depth--;
230
+ if ($depth === 0) {
231
+ return $currentLine;
232
+ }
233
+ }
234
+ continue;
235
+ }
236
+ if (!$seenBrace && $text === ';') {
237
+ return $currentLine;
238
+ }
239
+ }
240
+
241
+ return $currentLine;
242
+ }
243
+
244
+ function find_parameter_bounds(array $tokens, int $functionIndex): array {
245
+ $count = count($tokens);
246
+ $openIndex = null;
247
+
248
+ for ($index = $functionIndex; $index < $count; $index++) {
249
+ $text = token_text($tokens[$index]);
250
+ if ($text === '(') {
251
+ $openIndex = $index;
252
+ break;
253
+ }
254
+ if ($text === ';' || $text === '{') {
255
+ return [null, null];
256
+ }
257
+ }
258
+
259
+ if ($openIndex === null) {
260
+ return [null, null];
261
+ }
262
+
263
+ $depth = 0;
264
+ for ($index = $openIndex; $index < $count; $index++) {
265
+ $text = token_text($tokens[$index]);
266
+ if ($text === '(') {
267
+ $depth++;
268
+ continue;
269
+ }
270
+ if ($text === ')') {
271
+ $depth--;
272
+ if ($depth === 0) {
273
+ return [$openIndex, $index];
274
+ }
275
+ }
276
+ }
277
+
278
+ return [$openIndex, null];
279
+ }
280
+
281
+ function parameter_counts(array $tokens, ?int $openIndex, ?int $closeIndex): array {
282
+ if ($openIndex === null || $closeIndex === null || $closeIndex <= $openIndex + 1) {
283
+ return ['declared_args' => 0, 'optional_args' => 0];
284
+ }
285
+
286
+ $declaredArgs = 0;
287
+ $optionalArgs = 0;
288
+ $segmentHasVariable = false;
289
+ $segmentIsOptional = false;
290
+ $depth = 0;
291
+
292
+ $flushSegment = function () use (&$declaredArgs, &$optionalArgs, &$segmentHasVariable, &$segmentIsOptional): void {
293
+ if ($segmentHasVariable) {
294
+ $declaredArgs++;
295
+ if ($segmentIsOptional) {
296
+ $optionalArgs++;
297
+ }
298
+ }
299
+ $segmentHasVariable = false;
300
+ $segmentIsOptional = false;
301
+ };
302
+
303
+ for ($index = $openIndex + 1; $index < $closeIndex; $index++) {
304
+ $token = $tokens[$index];
305
+ $text = token_text($token);
306
+
307
+ if ($text === '(' || $text === '[' || $text === '{') {
308
+ $depth++;
309
+ continue;
310
+ }
311
+ if ($text === ')' || $text === ']' || $text === '}') {
312
+ $depth = max(0, $depth - 1);
313
+ continue;
314
+ }
315
+ if ($text === ',' && $depth === 0) {
316
+ $flushSegment();
317
+ continue;
318
+ }
319
+ if (is_array($token) && $token[0] === T_VARIABLE) {
320
+ $segmentHasVariable = true;
321
+ continue;
322
+ }
323
+ if ($text === '=' && $depth === 0) {
324
+ $segmentIsOptional = true;
325
+ continue;
326
+ }
327
+ if ($text === '...') {
328
+ $segmentIsOptional = true;
329
+ }
330
+ }
331
+
332
+ $flushSegment();
333
+
334
+ return ['declared_args' => $declaredArgs, 'optional_args' => $optionalArgs];
335
+ }
336
+
337
+ function is_visibility_token($token): bool {
338
+ return is_array($token) && in_array($token[0], [T_PUBLIC, T_PROTECTED, T_PRIVATE], true);
339
+ }
340
+
341
+ function add_class_property(array &$classDefinition, string $name, bool $isOptional): void {
342
+ foreach ($classDefinition['properties'] as &$property) {
343
+ if ($property['name'] === $name) {
344
+ $property['is_optional'] = $property['is_optional'] || $isOptional;
345
+ return;
346
+ }
347
+ }
348
+ unset($property);
349
+
350
+ $classDefinition['properties'][] = [
351
+ 'name' => $name,
352
+ 'is_optional' => $isOptional,
353
+ ];
354
+ }
355
+
356
+ function split_top_level_segments(array $tokens, int $startIndex, int $endIndex): array {
357
+ $segments = [];
358
+ $segmentStart = $startIndex;
359
+ $depth = 0;
360
+ for ($index = $startIndex; $index < $endIndex; $index++) {
361
+ $text = token_text($tokens[$index]);
362
+ if ($text === '(' || $text === '[' || $text === '{') {
363
+ $depth++;
364
+ continue;
365
+ }
366
+ if ($text === ')' || $text === ']' || $text === '}') {
367
+ $depth = max(0, $depth - 1);
368
+ continue;
369
+ }
370
+ if ($text === ',' && $depth === 0) {
371
+ $segments[] = array_slice($tokens, $segmentStart, $index - $segmentStart);
372
+ $segmentStart = $index + 1;
373
+ }
374
+ }
375
+
376
+ $segments[] = array_slice($tokens, $segmentStart, $endIndex - $segmentStart);
377
+ return $segments;
378
+ }
379
+
380
+ function segment_variable_name(array $segment): ?string {
381
+ foreach ($segment as $token) {
382
+ if (is_array($token) && $token[0] === T_VARIABLE) {
383
+ return ltrim($token[1], '$');
384
+ }
385
+ }
386
+ return null;
387
+ }
388
+
389
+ function segment_has_visibility(array $segment): bool {
390
+ foreach ($segment as $token) {
391
+ if (is_visibility_token($token)) {
392
+ return true;
393
+ }
394
+ }
395
+ return false;
396
+ }
397
+
398
+ function segment_has_optional_property_type(array $segment): bool {
399
+ foreach ($segment as $token) {
400
+ if (is_array($token) && $token[0] === T_VARIABLE) {
401
+ return false;
402
+ }
403
+ $text = strtolower(token_text($token));
404
+ if ($text === '?' || $text === 'null') {
405
+ return true;
406
+ }
407
+ }
408
+ return false;
409
+ }
410
+
411
+ function segment_is_optional_property(array $segment, bool $baseOptional = false): bool {
412
+ if ($baseOptional || segment_has_optional_property_type($segment)) {
413
+ return true;
414
+ }
415
+
416
+ $seenVariable = false;
417
+ $seenEquals = false;
418
+ foreach ($segment as $token) {
419
+ $text = strtolower(token_text($token));
420
+ if (is_array($token) && $token[0] === T_VARIABLE) {
421
+ $seenVariable = true;
422
+ continue;
423
+ }
424
+ if ($seenVariable && $text === '=') {
425
+ $seenEquals = true;
426
+ continue;
427
+ }
428
+ if ($seenEquals && $text === 'null') {
429
+ return true;
430
+ }
431
+ }
432
+ return false;
433
+ }
434
+
435
+ function promoted_properties(array $tokens, ?int $openIndex, ?int $closeIndex): array {
436
+ if ($openIndex === null || $closeIndex === null || $closeIndex <= $openIndex + 1) {
437
+ return [];
438
+ }
439
+
440
+ $properties = [];
441
+ foreach (split_top_level_segments($tokens, $openIndex + 1, $closeIndex) as $segment) {
442
+ if (!segment_has_visibility($segment)) {
443
+ continue;
444
+ }
445
+ $name = segment_variable_name($segment);
446
+ if ($name === null) {
447
+ continue;
448
+ }
449
+ $properties[] = [
450
+ 'name' => $name,
451
+ 'is_optional' => segment_is_optional_property($segment),
452
+ ];
453
+ }
454
+ return $properties;
455
+ }
456
+
457
+ function property_declaration_properties(array $tokens, int $startIndex): array {
458
+ $count = count($tokens);
459
+ $endIndex = $startIndex;
460
+ for (; $endIndex < $count; $endIndex++) {
461
+ $token = $tokens[$endIndex];
462
+ if (is_array($token) && $token[0] === T_FUNCTION) {
463
+ return [];
464
+ }
465
+ $text = token_text($token);
466
+ if ($text === ';' || $text === '{') {
467
+ break;
468
+ }
469
+ }
470
+
471
+ $statement = array_slice($tokens, $startIndex, $endIndex - $startIndex);
472
+ $baseOptional = segment_has_optional_property_type($statement);
473
+ $properties = [];
474
+ foreach (split_top_level_segments($tokens, $startIndex, $endIndex) as $segment) {
475
+ $name = segment_variable_name($segment);
476
+ if ($name === null) {
477
+ continue;
478
+ }
479
+ $properties[] = [
480
+ 'name' => $name,
481
+ 'is_optional' => segment_is_optional_property($segment, $baseOptional),
482
+ ];
483
+ }
484
+ return $properties;
485
+ }
486
+
487
+ function previous_significant_text(array $tokens, int $index): string {
488
+ for ($j = $index - 1; $j >= 0; $j--) {
489
+ $token = $tokens[$j];
490
+ if (is_array($token) && in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
491
+ continue;
492
+ }
493
+ return token_text($token);
494
+ }
495
+ return '';
496
+ }
497
+
498
+ function previous_significant_token(array $tokens, int $index) {
499
+ for ($j = $index - 1; $j >= 0; $j--) {
500
+ $token = $tokens[$j];
501
+ if (is_array($token) && in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
502
+ continue;
503
+ }
504
+ return $token;
505
+ }
506
+ return null;
507
+ }
508
+
509
+ function class_token_is_abstract(array $tokens, int $classIndex): bool {
510
+ $token = previous_significant_token($tokens, $classIndex);
511
+ return is_array($token) && $token[0] === T_ABSTRACT;
512
+ }
513
+
514
+ function member_is_abstract(array $tokens, int $functionIndex): bool {
515
+ for ($index = $functionIndex - 1; $index >= 0; $index--) {
516
+ $token = $tokens[$index];
517
+ if (!is_array($token)) {
518
+ $text = token_text($token);
519
+ if ($text === ';' || $text === '{' || $text === '}') {
520
+ break;
521
+ }
522
+ continue;
523
+ }
524
+
525
+ if (in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_FINAL], true)) {
526
+ continue;
527
+ }
528
+ return $token[0] === T_ABSTRACT;
529
+ }
530
+ return false;
531
+ }
532
+
533
+ function extract_file(string $path): array {
534
+ $content = file_get_contents($path);
535
+ $tokens = token_get_all($content);
536
+ $imports = [];
537
+ $classes = [];
538
+ $interfaces = [];
539
+ $types = [];
540
+ $abstractClasses = [];
541
+ $functions = [];
542
+ $classIndexByName = [];
543
+ $interfaceIndexByName = [];
544
+ $abstractClassIndexByName = [];
545
+
546
+ $count = count($tokens);
547
+ $currentLine = 1;
548
+ $braceDepth = 0;
549
+ $activeDefinitionKind = null;
550
+ $activeDefinitionName = null;
551
+ $activeDefinitionDepth = 0;
552
+ $statementId = 0;
553
+
554
+ for ($i = 0; $i < $count; $i++) {
555
+ $token = $tokens[$i];
556
+ $currentLine = token_line_value($token, $currentLine);
557
+
558
+ if ($token === '{') {
559
+ $braceDepth++;
560
+ continue;
561
+ }
562
+ if ($token === '}') {
563
+ if ($activeDefinitionName !== null && $braceDepth === $activeDefinitionDepth) {
564
+ $activeDefinitionKind = null;
565
+ $activeDefinitionName = null;
566
+ $activeDefinitionDepth = 0;
567
+ }
568
+ $braceDepth = max(0, $braceDepth - 1);
569
+ continue;
570
+ }
571
+ if (!is_array($token)) {
572
+ continue;
573
+ }
574
+
575
+ if ($token[0] === T_USE) {
576
+ $statementId++;
577
+ $imports = array_merge(
578
+ $imports,
579
+ use_statement_imports($tokens, $i, $statementId),
580
+ );
581
+ continue;
582
+ }
583
+
584
+ if ($token[0] === T_CLASS) {
585
+ $j = $i + 1;
586
+ while ($j < $count && token_text($tokens[$j]) !== '{') {
587
+ if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
588
+ $className = $tokens[$j][1];
589
+ if (class_token_is_abstract($tokens, $i)) {
590
+ $abstractClasses[] = [
591
+ 'name' => $className,
592
+ 'visibility' => 'public',
593
+ 'visibility_intent' => visibility_intent($className, 'public'),
594
+ 'abstract_methods' => [],
595
+ 'concrete_methods' => [],
596
+ 'properties' => [],
597
+ 'line_count' => max(
598
+ 0,
599
+ find_block_end_line($tokens, $j, $currentLine) - $currentLine + 1
600
+ ),
601
+ ];
602
+ $abstractClassIndexByName[$className] = count($abstractClasses) - 1;
603
+ $activeDefinitionKind = 'abstract_class';
604
+ } else {
605
+ $classes[] = [
606
+ 'name' => $className,
607
+ 'visibility' => 'public',
608
+ 'visibility_intent' => visibility_intent($className, 'public'),
609
+ 'methods' => [],
610
+ 'properties' => [],
611
+ 'line_count' => max(
612
+ 0,
613
+ find_block_end_line($tokens, $j, $currentLine) - $currentLine + 1
614
+ ),
615
+ ];
616
+ $classIndexByName[$className] = count($classes) - 1;
617
+ $activeDefinitionKind = 'class';
618
+ }
619
+ $activeDefinitionName = $className;
620
+ $activeDefinitionDepth = $braceDepth + 1;
621
+ break;
622
+ }
623
+ $j++;
624
+ }
625
+ continue;
626
+ }
627
+
628
+ if ($token[0] === T_INTERFACE) {
629
+ $j = $i + 1;
630
+ while ($j < $count && token_text($tokens[$j]) !== '{') {
631
+ if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
632
+ $interfaceName = $tokens[$j][1];
633
+ $interfaces[] = [
634
+ 'name' => $interfaceName,
635
+ 'visibility' => 'public',
636
+ 'visibility_intent' => visibility_intent($interfaceName, 'public'),
637
+ 'methods' => [],
638
+ 'properties' => [],
639
+ 'signatures' => [],
640
+ 'line_count' => max(
641
+ 0,
642
+ find_block_end_line($tokens, $j, $currentLine) - $currentLine + 1
643
+ ),
644
+ ];
645
+ $interfaceIndexByName[$interfaceName] = count($interfaces) - 1;
646
+ $activeDefinitionKind = 'interface';
647
+ $activeDefinitionName = $interfaceName;
648
+ $activeDefinitionDepth = $braceDepth + 1;
649
+ break;
650
+ }
651
+ $j++;
652
+ }
653
+ continue;
654
+ }
655
+
656
+ if (
657
+ $activeDefinitionName !== null
658
+ && in_array($activeDefinitionKind, ['class', 'abstract_class'], true)
659
+ && $braceDepth === $activeDefinitionDepth
660
+ && is_visibility_token($token)
661
+ && !in_array(previous_significant_text($tokens, $i), ['(', ','], true)
662
+ ) {
663
+ $definitionIndex = $activeDefinitionKind === 'class'
664
+ ? $classIndexByName[$activeDefinitionName]
665
+ : $abstractClassIndexByName[$activeDefinitionName];
666
+ foreach (property_declaration_properties($tokens, $i) as $property) {
667
+ if ($activeDefinitionKind === 'class') {
668
+ add_class_property(
669
+ $classes[$definitionIndex],
670
+ $property['name'],
671
+ $property['is_optional']
672
+ );
673
+ } else {
674
+ add_class_property(
675
+ $abstractClasses[$definitionIndex],
676
+ $property['name'],
677
+ $property['is_optional']
678
+ );
679
+ }
680
+ }
681
+ continue;
682
+ }
683
+
684
+ if ($token[0] === T_FUNCTION) {
685
+ $j = $i + 1;
686
+ while ($j < $count && token_text($tokens[$j]) !== '(') {
687
+ if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
688
+ $functionName = $tokens[$j][1];
689
+ $lineCount = max(
690
+ 0,
691
+ find_block_end_line($tokens, $j, $currentLine) - $currentLine + 1
692
+ );
693
+ [$openIndex, $closeIndex] = find_parameter_bounds($tokens, $i);
694
+ $parameterCounts = parameter_counts($tokens, $openIndex, $closeIndex);
695
+ if ($activeDefinitionKind === 'class' && $activeDefinitionName !== null && array_key_exists($activeDefinitionName, $classIndexByName)) {
696
+ if ($functionName === '__construct') {
697
+ foreach (promoted_properties($tokens, $openIndex, $closeIndex) as $property) {
698
+ add_class_property(
699
+ $classes[$classIndexByName[$activeDefinitionName]],
700
+ $property['name'],
701
+ $property['is_optional']
702
+ );
703
+ }
704
+ }
705
+ $visibility = member_visibility($tokens, $i);
706
+ $classes[$classIndexByName[$activeDefinitionName]]['methods'][] = [
707
+ 'name' => $functionName,
708
+ 'visibility' => $visibility,
709
+ 'visibility_intent' => visibility_intent($functionName, $visibility),
710
+ 'line_count' => $lineCount,
711
+ 'declared_args' => $parameterCounts['declared_args'],
712
+ 'optional_args' => $parameterCounts['optional_args'],
713
+ ];
714
+ } elseif ($activeDefinitionKind === 'abstract_class' && $activeDefinitionName !== null && array_key_exists($activeDefinitionName, $abstractClassIndexByName)) {
715
+ if ($functionName === '__construct') {
716
+ foreach (promoted_properties($tokens, $openIndex, $closeIndex) as $property) {
717
+ add_class_property(
718
+ $abstractClasses[$abstractClassIndexByName[$activeDefinitionName]],
719
+ $property['name'],
720
+ $property['is_optional']
721
+ );
722
+ }
723
+ }
724
+ $visibility = member_visibility($tokens, $i);
725
+ $methodDefinition = [
726
+ 'name' => $functionName,
727
+ 'visibility' => $visibility,
728
+ 'visibility_intent' => visibility_intent($functionName, $visibility),
729
+ 'line_count' => $lineCount,
730
+ 'declared_args' => $parameterCounts['declared_args'],
731
+ 'optional_args' => $parameterCounts['optional_args'],
732
+ ];
733
+ if (member_is_abstract($tokens, $i)) {
734
+ $abstractClasses[$abstractClassIndexByName[$activeDefinitionName]]['abstract_methods'][] = $methodDefinition;
735
+ } else {
736
+ $abstractClasses[$abstractClassIndexByName[$activeDefinitionName]]['concrete_methods'][] = $methodDefinition;
737
+ }
738
+ } elseif ($activeDefinitionKind === 'interface' && $activeDefinitionName !== null && array_key_exists($activeDefinitionName, $interfaceIndexByName)) {
739
+ $visibility = member_visibility($tokens, $i);
740
+ $interfaces[$interfaceIndexByName[$activeDefinitionName]]['methods'][] = [
741
+ 'name' => $functionName,
742
+ 'visibility' => $visibility,
743
+ 'visibility_intent' => visibility_intent($functionName, $visibility),
744
+ 'line_count' => $lineCount,
745
+ 'declared_args' => $parameterCounts['declared_args'],
746
+ 'optional_args' => $parameterCounts['optional_args'],
747
+ ];
748
+ } else {
749
+ $functions[] = [
750
+ 'name' => $functionName,
751
+ 'visibility' => 'public',
752
+ 'visibility_intent' => visibility_intent($functionName, 'public'),
753
+ 'line_count' => $lineCount,
754
+ 'declared_args' => $parameterCounts['declared_args'],
755
+ 'optional_args' => $parameterCounts['optional_args'],
756
+ ];
757
+ }
758
+ break;
759
+ }
760
+ $j++;
761
+ }
762
+ }
763
+ }
764
+
765
+ $publicMethodCounts = array_map(
766
+ fn ($classDefinition) => count(array_filter(
767
+ $classDefinition['methods'],
768
+ fn ($methodDefinition) => $methodDefinition['visibility'] === 'public',
769
+ )),
770
+ $classes
771
+ );
772
+ $publicAbstractClassMethodCounts = array_map(
773
+ fn ($classDefinition) => count(array_filter(
774
+ array_merge($classDefinition['abstract_methods'], $classDefinition['concrete_methods']),
775
+ fn ($methodDefinition) => $methodDefinition['visibility'] === 'public',
776
+ )),
777
+ $abstractClasses
778
+ );
779
+
780
+ return [
781
+ 'imports' => $imports,
782
+ 'classes' => $classes,
783
+ 'interfaces' => $interfaces,
784
+ 'types' => $types,
785
+ 'abstract_classes' => $abstractClasses,
786
+ 'functions' => $functions,
787
+ 'line_count' => line_count_for_content($content),
788
+ 'code_line_count' => code_line_count_for_content($content),
789
+ 'public_symbol_count' => count($classes) + count($interfaces) + count($abstractClasses) + count($functions) + array_sum($publicMethodCounts) + array_sum($publicAbstractClassMethodCounts),
790
+ ];
791
+ }
792
+
793
+ function extract_batch_from_stdin(): array {
794
+ $payload = stream_get_contents(STDIN);
795
+ $pathsBySourceId = json_decode($payload, true);
796
+ if (!is_array($pathsBySourceId)) {
797
+ fwrite(STDERR, "Expected a JSON object mapping source ids to PHP file paths.\n");
798
+ exit(1);
799
+ }
800
+
801
+ $result = [];
802
+ foreach ($pathsBySourceId as $sourceId => $path) {
803
+ if (!is_string($sourceId) || !is_string($path)) {
804
+ fwrite(STDERR, "Expected source ids and paths to be strings.\n");
805
+ exit(1);
806
+ }
807
+ $result[$sourceId] = extract_file($path);
808
+ }
809
+ return $result;
810
+ }
811
+
812
+ if (($argv[1] ?? null) === '--batch') {
813
+ echo json_encode(extract_batch_from_stdin());
814
+ } else {
815
+ echo json_encode(extract_file($argv[1]));
816
+ }