@@ -29,6 +29,201 @@ interface BackedEnum extends UnitEnum
2929}
3030" ;
3131
32+ // ─── Function stubs ─────────────────────────────────────────────────────────
33+ // Minimal PHP stubs for built-in functions grouped by extension/category.
34+
35+ static ARRAY_FUNCTIONS_STUB : & str = "\
36+ <?php
37+ /**
38+ * @param callable|null $callback
39+ * @param array $array
40+ * @param array ...$arrays
41+ * @return array
42+ */
43+ function array_map(?callable $callback, array $array, array ...$arrays): array {}
44+
45+ /**
46+ * @param array &$array
47+ * @return mixed
48+ */
49+ function array_pop(array &$array): mixed {}
50+
51+ /**
52+ * @param array &$array
53+ * @param mixed ...$values
54+ * @return int
55+ */
56+ function array_push(array &$array, mixed ...$values): int {}
57+
58+ /**
59+ * @param string|int $key
60+ * @param array $array
61+ * @return bool
62+ */
63+ function array_key_exists(string|int $key, array $array): bool {}
64+ " ;
65+
66+ static STRING_FUNCTIONS_STUB : & str = "\
67+ <?php
68+ /**
69+ * @param string $haystack
70+ * @param string $needle
71+ * @return bool
72+ */
73+ function str_contains(string $haystack, string $needle): bool {}
74+
75+ /**
76+ * @param string $string
77+ * @param int $offset
78+ * @param int|null $length
79+ * @return string
80+ */
81+ function substr(string $string, int $offset, ?int $length = null): string {}
82+ " ;
83+
84+ static JSON_FUNCTIONS_STUB : & str = "\
85+ <?php
86+ /**
87+ * @param string $json
88+ * @param bool|null $associative
89+ * @param int $depth
90+ * @param int $flags
91+ * @return mixed
92+ */
93+ function json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0): mixed {}
94+ " ;
95+
96+ static DATE_FUNCTIONS_STUB : & str = "\
97+ <?php
98+ /**
99+ * @param string|null $datetime
100+ * @param DateTimeZone|null $timezone
101+ * @return DateTime|false
102+ */
103+ function date_create(?string $datetime = \" now\" , ?DateTimeZone $timezone = null): DateTime|false {}
104+ " ;
105+
106+ static SIMPLEXML_FUNCTIONS_STUB : & str = "\
107+ <?php
108+ /**
109+ * @param string $data
110+ * @param string|null $class_name
111+ * @param int $options
112+ * @param string $namespace_or_prefix
113+ * @param bool $is_prefix
114+ * @return SimpleXMLElement|false
115+ */
116+ function simplexml_load_string(string $data, ?string $class_name = null, int $options = 0, string $namespace_or_prefix = \" \" , bool $is_prefix = false): SimpleXMLElement|false {}
117+ " ;
118+
119+ static PCRE_FUNCTIONS_STUB : & str = "\
120+ <?php
121+ /**
122+ * @param string $pattern
123+ * @param string $subject
124+ * @param array|null &$matches
125+ * @param int $flags
126+ * @param int $offset
127+ * @return int|false
128+ */
129+ function preg_match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int|false {}
130+ " ;
131+
132+ // ─── Class stubs ────────────────────────────────────────────────────────────
133+
134+ static DATETIME_CLASS_STUB : & str = "\
135+ <?php
136+ class DateTime
137+ {
138+ public function __construct(?string $datetime = \" now\" , ?DateTimeZone $timezone = null) {}
139+
140+ /**
141+ * @param string $format
142+ * @return string
143+ */
144+ public function format(string $format): string {}
145+
146+ /**
147+ * @param string $modifier
148+ * @return DateTime|false
149+ */
150+ public function modify(string $modifier): DateTime|false {}
151+
152+ /**
153+ * @return int
154+ */
155+ public function getTimestamp(): int {}
156+
157+ /**
158+ * @param int $year
159+ * @param int $month
160+ * @param int $day
161+ * @return DateTime
162+ */
163+ public function setDate(int $year, int $month, int $day): DateTime {}
164+
165+ /**
166+ * @param int $hour
167+ * @param int $minute
168+ * @param int $second
169+ * @param int $microsecond
170+ * @return DateTime
171+ */
172+ public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime {}
173+ }
174+ " ;
175+
176+ static SIMPLEXMLELEMENT_CLASS_STUB : & str = "\
177+ <?php
178+ class SimpleXMLElement
179+ {
180+ /**
181+ * @param string $expression
182+ * @return array|false|null
183+ */
184+ public function xpath(string $expression): array|false|null {}
185+
186+ /**
187+ * @param string|null $namespaceOrPrefix
188+ * @param bool $isPrefix
189+ * @return SimpleXMLElement|null
190+ */
191+ public function children(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement {}
192+
193+ /**
194+ * @param string|null $namespaceOrPrefix
195+ * @param bool $isPrefix
196+ * @return SimpleXMLElement|null
197+ */
198+ public function attributes(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement {}
199+
200+ /**
201+ * @param string $qualifiedName
202+ * @param string|null $value
203+ * @param string|null $namespace
204+ * @return SimpleXMLElement|null
205+ */
206+ public function addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null): ?SimpleXMLElement {}
207+
208+ /**
209+ * @return string
210+ */
211+ public function getName(): string {}
212+ }
213+ " ;
214+
215+ // ─── Constant stubs ─────────────────────────────────────────────────────────
216+
217+ static CONSTANTS_STUB : & str = "\
218+ <?php
219+ define('PHP_EOL', \" \\ n\" );
220+ define('PHP_INT_MAX', 9223372036854775807);
221+ define('PHP_INT_MIN', -9223372036854775808);
222+ define('PHP_MAJOR_VERSION', 8);
223+ define('SORT_ASC', 4);
224+ define('SORT_DESC', 3);
225+ " ;
226+
32227/// Create a test backend whose `stub_index` contains minimal `UnitEnum`
33228/// and `BackedEnum` stubs. This makes "embedded stub" tests fully
34229/// self-contained — they no longer require a prior `composer install`.
@@ -39,6 +234,48 @@ pub fn create_test_backend_with_stubs() -> Backend {
39234 Backend :: new_test_with_stubs ( stubs)
40235}
41236
237+ /// Create a test backend with embedded PHP stubs for built-in functions,
238+ /// classes, and constants. This makes the stub-function tests fully
239+ /// self-contained — they work whether or not phpstorm-stubs are installed.
240+ pub fn create_test_backend_with_function_stubs ( ) -> Backend {
241+ // ── Class stubs ──
242+ let mut class_stubs: HashMap < & ' static str , & ' static str > = HashMap :: new ( ) ;
243+ class_stubs. insert ( "DateTime" , DATETIME_CLASS_STUB ) ;
244+ class_stubs. insert ( "SimpleXMLElement" , SIMPLEXMLELEMENT_CLASS_STUB ) ;
245+ class_stubs. insert ( "UnitEnum" , UNIT_ENUM_STUB ) ;
246+ class_stubs. insert ( "BackedEnum" , BACKED_ENUM_STUB ) ;
247+
248+ // ── Function stubs ──
249+ let mut function_stubs: HashMap < & ' static str , & ' static str > = HashMap :: new ( ) ;
250+ // Array functions (all point to the same source)
251+ function_stubs. insert ( "array_map" , ARRAY_FUNCTIONS_STUB ) ;
252+ function_stubs. insert ( "array_pop" , ARRAY_FUNCTIONS_STUB ) ;
253+ function_stubs. insert ( "array_push" , ARRAY_FUNCTIONS_STUB ) ;
254+ function_stubs. insert ( "array_key_exists" , ARRAY_FUNCTIONS_STUB ) ;
255+ // String functions
256+ function_stubs. insert ( "str_contains" , STRING_FUNCTIONS_STUB ) ;
257+ function_stubs. insert ( "substr" , STRING_FUNCTIONS_STUB ) ;
258+ // JSON functions
259+ function_stubs. insert ( "json_decode" , JSON_FUNCTIONS_STUB ) ;
260+ // Date functions
261+ function_stubs. insert ( "date_create" , DATE_FUNCTIONS_STUB ) ;
262+ // SimpleXML functions
263+ function_stubs. insert ( "simplexml_load_string" , SIMPLEXML_FUNCTIONS_STUB ) ;
264+ // PCRE functions
265+ function_stubs. insert ( "preg_match" , PCRE_FUNCTIONS_STUB ) ;
266+
267+ // ── Constant stubs ──
268+ let mut constant_stubs: HashMap < & ' static str , & ' static str > = HashMap :: new ( ) ;
269+ constant_stubs. insert ( "PHP_EOL" , CONSTANTS_STUB ) ;
270+ constant_stubs. insert ( "PHP_INT_MAX" , CONSTANTS_STUB ) ;
271+ constant_stubs. insert ( "PHP_INT_MIN" , CONSTANTS_STUB ) ;
272+ constant_stubs. insert ( "PHP_MAJOR_VERSION" , CONSTANTS_STUB ) ;
273+ constant_stubs. insert ( "SORT_ASC" , CONSTANTS_STUB ) ;
274+ constant_stubs. insert ( "SORT_DESC" , CONSTANTS_STUB ) ;
275+
276+ Backend :: new_test_with_all_stubs ( class_stubs, function_stubs, constant_stubs)
277+ }
278+
42279/// Helper: create a temp workspace with a composer.json and PHP files,
43280/// then return a Backend configured with that workspace root + PSR-4 mappings.
44281pub fn create_psr4_workspace (
0 commit comments