Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReflectionHelpers
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 3
756
0.00% covered (danger)
0.00%
0 / 1
 getTypeString
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
72
 extractDocTagMessage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getArguments
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
306
1<?php
2
3namespace Lucent\StaticAnalysis;
4
5use ReflectionIntersectionType;
6use ReflectionMethod;
7use ReflectionNamedType;
8use ReflectionParameter;
9use ReflectionType;
10use ReflectionUnionType;
11
12class ReflectionHelpers
13{
14
15    /**
16     * Get a string representation of a parameter type
17     *
18     * @param ReflectionType|ReflectionUnionType|ReflectionIntersectionType|null $type The parameter type from reflection
19     * @param ReflectionParameter|ReflectionMethod $caller
20     * @return string A string representation of the type
21     */
22    public static function getTypeString(null|ReflectionType|ReflectionUnionType|ReflectionIntersectionType $type, ReflectionParameter|ReflectionMethod $caller): string {
23
24        $returnType = "unknown";
25
26        // For union types use "|" separator
27        if ($type instanceof ReflectionUnionType) {
28            $returnType = implode('|', $type->getTypes());
29        }
30        // For intersection types use "&" separator
31        else if ($type instanceof ReflectionIntersectionType) {
32            $returnType = implode('&', $type->getTypes());
33        }
34
35        if ($type === null) {
36            $returnType =  "void";
37        }
38
39        if ($type instanceof ReflectionNamedType) {
40            $returnType =  $type->getName();
41        }
42
43        if($returnType === "static" || $returnType === "self"){
44            $returnType = $caller->getDeclaringClass()->getName();
45        }
46
47        if($returnType === null){
48            $returnType = "void";
49        }
50
51        return $returnType;
52    }
53
54    /**
55     * Extracts a specified tag's message from a PHPDoc block.
56     *
57     * @param string $docBlock The PHPDoc block as a string
58     * @param string $tagName The PHPDoc tag to extract (without the @ symbol)
59     * @return string|null The extracted message or null if not found
60     */
61    public static function extractDocTagMessage(string $docBlock, string $tagName): ?string
62    {
63        // Use regex to find the specified tag and capture the message
64        $pattern = '/@' . preg_quote($tagName, '/') . '\s+(.*?)(\n\s*\*|\n\s*\/|\s*$)/s';
65
66        if (preg_match($pattern, $docBlock, $matches)) {
67            // Clean up the message - remove leading/trailing whitespace and asterisks
68            $message = $matches[1];
69
70            // Remove any line breaks and following asterisks/spaces
71            $message = preg_replace('/\n\s*\*\s*/', ' ', $message);
72
73            // Remove any remaining whitespace and normalize spaces
74            $message = preg_replace('/\s+/', ' ', $message);
75
76            return trim($message);
77        }
78
79        return null;
80    }
81
82    /**
83     * Extract method arguments from token array
84     *
85     * Parses tokens to extract argument values, types, and positions
86     *
87     * @param array $tokens Array of tokens from token_get_all
88     * @param int $startingIndex Index where arguments begin in the token array
89     * @return array List of arguments with their values, types, and positions
90     */
91    public static function getArguments(array $tokens, int $startingIndex): array
92    {
93        $args = [];
94        $i = $startingIndex;
95        $currentArgument = null;
96        $argumentIndex = 0;
97        $inParenthesis = 0;
98        $reachedOpenParen = false;
99
100        // Find the opening parenthesis first
101        while ($i < count($tokens)) {
102            if ($tokens[$i] === '(') {
103                $reachedOpenParen = true;
104                break;
105            }
106            $i++;
107        }
108
109        if (!$reachedOpenParen) {
110            return [];
111        }
112
113        $i++; // Skip the opening parenthesis
114
115        // Now collect arguments until we reach the closing parenthesis
116        while ($i < count($tokens)) {
117            $token = $tokens[$i];
118
119            // Check if we're at an opening parenthesis (nested calls)
120            if ($token === '(') {
121                $inParenthesis++;
122            }
123
124            // Check if we're at a closing parenthesis
125            else if ($token === ')') {
126                if ($inParenthesis === 0) {
127                    // If we have a current argument, add it
128                    if ($currentArgument !== null) {
129                        $args[$argumentIndex] = $currentArgument;
130                    }
131                    break; // End of arguments
132                }
133                $inParenthesis--;
134            }
135
136            // Check if we're at a comma (argument separator)
137            else if ($token === ',' && $inParenthesis === 0) {
138                if ($currentArgument !== null) {
139                    $args[$argumentIndex] = $currentArgument;
140                    $argumentIndex++;
141                    $currentArgument = null;
142                }
143            }
144
145            // Otherwise, collect token as part of the current argument
146            else {
147                // Add the token to the current argument
148                if ($currentArgument === null) {
149                    $currentArgument = [
150                        "type" => is_array($token) ? $token[0] : null,
151                        "value" => is_array($token) ? $token[1] : $token, // Here's where line 101 was likely failing
152                        "line" => is_array($token) && isset($token[2]) ? $token[2] : null
153                    ];
154                }
155            }
156
157            $i++;
158        }
159
160        return $args;
161    }
162}