feat: add CodeIgniter detector & analyzer#1335
Conversation
CodeIgniter 4 router DSL ($routes->get/post/match/add/resource/presenter/
group/environment) plus CI3 $route['…'] config parsing. Path placeholders
((:num), (:any), (:segment), (:alpha), (:alphanum), (:hash)) are normalized
to {name} braces.
There was a problem hiding this comment.
Code Review
This pull request introduces support for the CodeIgniter framework (versions 3 and 4) by adding a new detector and analyzer. The implementation includes logic for parsing routes, groups, resources, and presenters, along with corresponding functional and unit tests. Feedback focuses on ensuring that group prefixes and resource names are normalized to handle placeholders correctly and that CodeIgniter 3 routes without explicit method qualifiers are expanded to include all standard HTTP verbs.
| working_content.scan(group_pattern).each do |match| | ||
| group_prefix = match[1] | ||
| group_content = match[2] | ||
| new_prefix = build_full_path(prefix, group_prefix) |
There was a problem hiding this comment.
The group_prefix should be normalized before being used to build the full path. CodeIgniter 4 allows placeholders like (:segment) or (:any) in group prefixes. Without normalization, these placeholders will remain in the final endpoint path in their raw format (e.g., /api/(:segment)/users) instead of the standard brace format (e.g., /api/{segment}/users), which may affect parameter detection.
new_prefix = build_full_path(prefix, normalize_route(group_prefix))
| resource_pattern = /\$routes->resource\s*\(\s*['"]([^'"]+)['"][^)]*\)/mi | ||
| working_content.scan(resource_pattern).each do |match| | ||
| resource_name = match[1] | ||
| full_resource_path = build_full_path(prefix, resource_name) |
There was a problem hiding this comment.
Similar to group prefixes, the resource_name should be normalized. While typically a simple string, CodeIgniter allows more complex paths in resource definitions which might include placeholders. Normalizing ensures consistency in the emitted endpoint paths.
full_resource_path = build_full_path(prefix, normalize_route(resource_name))
| presenter_pattern = /\$routes->presenter\s*\(\s*['"]([^'"]+)['"][^)]*\)/mi | ||
| working_content.scan(presenter_pattern).each do |match| | ||
| resource_name = match[1] | ||
| full_resource_path = build_full_path(prefix, resource_name) |
| method = method_qualifier ? method_qualifier.to_s.upcase : "GET" | ||
| normalized = normalize_route(route_path) | ||
| params = extract_ci_path_params(normalized) | ||
| endpoints << Endpoint.new(normalized, method, params, details.dup) |
There was a problem hiding this comment.
In CodeIgniter 3, if a route is defined without an explicit HTTP verb qualifier (e.g., $route['products'] = '...'), it matches all HTTP methods. The current implementation defaults to GET, which significantly underestimates the attack surface. It should instead emit all standard HTTP verbs, similar to how $routes->add() is handled in the CI4 analyzer.
normalized = normalize_route(route_path)
params = extract_ci_path_params(normalized)
if method_qualifier
endpoints << Endpoint.new(normalized, method_qualifier.to_s.upcase, params, details.dup)
else
["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"].each do |method|
endpoints << Endpoint.new(normalized, method, params, details.dup)
end
end
…routes
- Pass group/resource/presenter names through normalize_route so routes
like group('tenant/(:num)', ...) emit /tenant/{num}/... instead of a
literal placeholder URL.
- CI3 $route['x'] = '...' now expands to all HTTP verbs (CI3 default);
the second [...] index is only treated as a verb when it is one of the
known CI3 verbs, avoiding misreading config keys like ['namespace'].
- Parenthesize the ResourceController + RESTful detector clause for clarity.
- Extend the fixture with a nested group and a placeholder-prefixed group.
Summary
php_codeigniterdetector and analyzer covering CodeIgniter 4 (app/Config/Routes.php) and CodeIgniter 3 (application/config/routes.php).composer.json(codeigniter4/framework), thesparkCLI, the routes config files, andCodeIgniter\namespace usage. Generic markers likeextends BaseControllerare intentionally avoided (Laravel uses the same alias).$routes->get/post/put/patch/delete/options/head,match,add,resource,presenter,group(prefix, [opts]?, fn),environment. CI3 entries ($route['products/(:num)'] = '...') are also supported.(:num)(:any)(:segment)(:alpha)(:alphanum)(:hash)are normalized to{name}braces and emitted as path params. Group bodies are stripped before sibling-level scans to avoid double-counting.Php::CodeIgniterintosrc/detector/detector.cr,src/analyzer/analyzer.cr, andsrc/techs/techs.cr(php_codeigniter).Test plan
crystal spec spec/unit_test/detector/php/codeigniter_detector_spec.cr(6 examples)crystal spec spec/functional_test/testers/php/codeigniter_spec.cr(96 examples — fixture covers verbs, match/add, resource, presenter, group with/without options, environment)crystal spec— full suite, 7119 examples, 0 failures