From 29ed19f1e41a8c512aab03ece513de82f52887ee Mon Sep 17 00:00:00 2001 From: nikhil2004 Date: Wed, 23 Sep 2026 15:00:01 +0530 Subject: [PATCH] fix(php): allow relative prefixes in route groups and correctly fail tests on extractor error --- graphify/extractors/engine.py | 10 +++++----- tests/test_extract_php_closures.py | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/graphify/extractors/engine.py b/graphify/extractors/engine.py index 0768ec96f..44531a856 100644 --- a/graphify/extractors/engine.py +++ b/graphify/extractors/engine.py @@ -3496,9 +3496,9 @@ def _php_get_route_name(closure_node, src: bytes) -> str | None: else: return None # Not a valid route closure else: - # Outer calls (e.g. group(), prefix()) just contribute their prefix if it starts with '/' - if path_text is not None and path_text.startswith("/"): - prefixes.append(path_text) + # Outer calls (e.g. group(), prefix()) contribute their prefix, normalized to start with '/' + if path_text is not None and path_text: + prefixes.append(path_text if path_text.startswith("/") else "/" + path_text) # Process any fluent method chain prefixes on the same statement fluent = call.child_by_field_name("object") @@ -3510,8 +3510,8 @@ def _php_get_route_name(closure_node, src: bytes) -> str | None: for cc in c.children: if cc.type in ("string", "encapsed_string"): f_path = _read_text(cc, src).strip("'\"") - if f_path.startswith("/"): - prefixes.append(f_path) + if f_path: + prefixes.append(f_path if f_path.startswith("/") else "/" + f_path) break fluent = fluent.child_by_field_name("object") diff --git a/tests/test_extract_php_closures.py b/tests/test_extract_php_closures.py index 1d8f5a2d3..4877c5d9e 100644 --- a/tests/test_extract_php_closures.py +++ b/tests/test_extract_php_closures.py @@ -18,7 +18,10 @@ $app->post('/api/users', function() { res = extract_php(php_file) if res.get("error"): - pytest.skip(res["error"]) + if "No language parser" in res["error"]: + pytest.skip("PHP grammar not installed") + else: + pytest.fail(res["error"]) labels = {n["label"] for n in res["nodes"]} @@ -37,7 +40,10 @@ $fn2 = function() { return 'hello'; }; res = extract_php(php_file) if res.get("error"): - pytest.skip(res["error"]) + if "No language parser" in res["error"]: + pytest.skip("PHP grammar not installed") + else: + pytest.fail(res["error"]) labels = {n["label"] for n in res["nodes"]} @@ -60,7 +66,10 @@ $app->group('/api/v1', function ($group) { res = extract_php(php_file) if res.get("error"): - pytest.skip(res["error"]) + if "No language parser" in res["error"]: + pytest.skip("PHP grammar not installed") + else: + pytest.fail(res["error"]) labels = {n["label"] for n in res["nodes"]} assert "GET /api/v1/users/{id}()" in labels, "Expected nested route closure to compose prefix" @@ -77,7 +86,10 @@ $value = $cache->get('user:42', function () { return 2; }); res = extract_php(php_file) if res.get("error"): - pytest.skip(res["error"]) + if "No language parser" in res["error"]: + pytest.skip("PHP grammar not installed") + else: + pytest.fail(res["error"]) labels = {n["label"] for n in res["nodes"]} assert "{closure#1}()" in labels, "Expected non-routing get() to fallback to ordinal"