Resolve a class identifier to a fully-qualified registry key. * - Fully-qualified ("\\Foo\\Bar"): FOO.BAR — strip leading slash. * - Aliased / single segment matches a use: the use target. * - First segment matches a use: substitute and append remainder. * - Otherwise: prefix with current_namespace_qn (or module). * * Returns an ar
| 199 | * |
| 200 | * Returns an arena-allocated string in dotted form. May return NULL for builtins. */ |
| 201 | const char *php_resolve_class_name(PHPLSPContext *ctx, const char *name) { |
| 202 | if (!name || !*name) |
| 203 | return NULL; |
| 204 | |
| 205 | /* Self / static / parent are class-relative pseudo-types and must be |
| 206 | * checked BEFORE the builtin-name check, since they're listed as |
| 207 | * builtins above for type-parsing purposes. */ |
| 208 | if (strcmp(name, "self") == 0 || strcmp(name, "static") == 0) { |
| 209 | return ctx->enclosing_class_qn; |
| 210 | } |
| 211 | if (strcmp(name, "parent") == 0) { |
| 212 | return ctx->enclosing_parent_qn; |
| 213 | } |
| 214 | if (php_is_builtin_type_name(name)) |
| 215 | return NULL; |
| 216 | |
| 217 | /* Fully-qualified — leading backslash. */ |
| 218 | if (name[0] == '\\') { |
| 219 | return php_ns_to_dot(ctx->arena, name + 1); |
| 220 | } |
| 221 | |
| 222 | /* Split first segment. */ |
| 223 | const char *sep = name; |
| 224 | while (*sep && *sep != '\\') |
| 225 | sep++; |
| 226 | char *first; |
| 227 | if (*sep) { |
| 228 | first = cbm_arena_strndup(ctx->arena, name, (size_t)(sep - name)); |
| 229 | } else { |
| 230 | first = cbm_arena_strdup(ctx->arena, name); |
| 231 | } |
| 232 | |
| 233 | const char *use_target = find_use(ctx, first, CBM_PHP_USE_CLASS); |
| 234 | if (use_target) { |
| 235 | if (*sep) { |
| 236 | /* use App\\Models as M; M\\User -> App.Models.User */ |
| 237 | return cbm_arena_sprintf(ctx->arena, "%s%s", use_target, |
| 238 | php_ns_to_dot(ctx->arena, sep)); |
| 239 | } |
| 240 | return use_target; |
| 241 | } |
| 242 | |
| 243 | /* Fallback for a same-file class reference: prefer module_qn-prefixed QN |
| 244 | * because that's what the unified extractor records. The PHP namespace |
| 245 | * declaration is *not* incorporated into the unified extractor's QNs |
| 246 | * (defs are keyed by file path + class name), so building from |
| 247 | * current_namespace_qn here would diverge and miss every same-file |
| 248 | * lookup. */ |
| 249 | if (ctx->module_qn && ctx->module_qn[0]) { |
| 250 | return cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->module_qn, |
| 251 | php_ns_to_dot(ctx->arena, name)); |
| 252 | } |
| 253 | return php_ns_to_dot(ctx->arena, name); |
| 254 | } |
| 255 | |
| 256 | /* Try to find a registered type for a "namespaced" QN. |
| 257 | * |
no test coverage detected