class
Analyzer::Php::Phalcon
Overview
Phalcon (https://phalcon.io/) is a PHP framework implemented as a C extension. It exposes two independent routing styles that real apps mix freely in the same codebase:
- Micro — a Slim-style DSL:
$app->get('/path', function () {...});, optionally grouped withPhalcon\Mvc\Micro\Collection. - MVC —
Phalcon\Mvc\Router::add()(+addGet/addPost/…, optionally grouped withPhalcon\Mvc\Router\Group), PHPDoc annotation routing (@RoutePrefix/@Get/@Route) and convention-based controller/action dispatch (ProductsController::showAction->/products/show).
Defined in:
analyzer/analyzers/php/phalcon.crConstant Summary
-
ACTION_METHOD_RE =
/public\s+function\s+(\w+)Action\s*\(([^)]*)\)/ -
ADD_REGEX =
/\$(\w+)->add(?!Get|Post|Put|Patch|Delete|Options|Head|Purge)\s*\(/i -
- MVC Router / Group generic add(), optionally chained with via(): $router->add('/products/update', 'Products::update')->via(['POST', 'PUT']); $blog->add('/save', ['action' => 'save']); Negative lookahead excludes addGet/addPost/... (case 3 above) and addPurge (a real Phalcon Router method for the non-standard PURGE verb, which this analyzer doesn't otherwise model — better to emit nothing for it than to mis-report it as an unrestricted GET route).
-
ADD_VERB_REGEX =
/\$(\w+)->add(Get|Post|Put|Patch|Delete|Options|Head)\s*\(\s*['"]([^'"\r\n]+)['"]/i -
- MVC Router explicit-verb helpers: $router->addGet('/path', 'Products::edit');
-
ANNOTATION_ROUTE_RE =
/@Route\s*\(\s*['"]([^'"]+)['"]([^)]*)\)/m -
ANNOTATION_VERB_RE =
/@(Get|Post|Put|Patch|Delete|Options|Head)\s*\(\s*['"]([^'"]+)['"]/ -
- PHPDoc annotation routing:
/**
- @RoutePrefix('/api/products') / class ProductsController extends Controller { /* @Get('/search') / public function searchAction() {} /* @Route('/save', methods={'POST', 'PUT'}) */ public function saveAction() {} }
- PHPDoc annotation routing:
/**
-
CONTROLLER_CLASS_RE =
/class\s+(\w+)Controller\s+extends\s+\\?([\w\\]+)\b[^{]*\{/ -
-
Convention-based controller/action dispatch: a public
fooActionmethod on a controller extendingPhalcon\Mvc\Controllermaps to/{controller}/{action}(default actionindexis omitted), with any action parameters appended as positional path segments —showAction($id)onProductsController->/products/show/{id}.Skipped entirely for controllers that already use PHPDoc annotation routing (case 5): an app wired with
RouterAnnotationstypically replaces the default convention router outright, so guessing convention routes alongside real annotation routes would invent endpoints that are never actually reachable.
-
-
HTTP_METHODS =
["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] -
MAP_CALL_RE =
/\$(\w+)->map\s*\(/i -
- map()/via(): $app->map('/repos/store/refs', 'actionProduct')->via(['GET', 'POST']);
Matched by call boundary (not a single-line path+comma regex) because
the trailing
->via(...)lookup needs the position right after the map() call's own closing paren — which a non-closure handler (a bare string/callable-array, with no body to bound) doesn't otherwise give us.
- map()/via(): $app->map('/repos/store/refs', 'actionProduct')->via(['GET', 'POST']);
Matched by call boundary (not a single-line path+comma regex) because
the trailing
-
NON_ROUTE_RECEIVERS =
Set {"config", "di", "container", "session", "cookies", "request", "response", "validator", "validation", "flash", "filter", "tag", "assets", "view", "dispatcher", "acl", "security", "crypt", "cache", "eventsmanager", "logger", "translate", "url", "escaper", "modelsmanager", "transactionmanager", "annotations"} -
->get(...)/->add(...)are not unique to routing in a Phalcon app:Phalcon\Config/Phalcon\Diobjects answer to->get($key, $default)(a 2-arg call shape identical to a route registration) andPhalcon\Validationanswers to->add($field, $validator). Both are extremely common in real Phalcon code — aconfig->get('adapter', 'Unknown')orvalidator->add('email', new Uniqueness(...))call in a model or service provider would otherwise read as a phantom route. These are the receiver names real Phalcon apps conventionally use for those non-routing services; a variable holding an actual Micro app, Router or route Collection/Group is never named one of these. -
PARAM_PATTERNS =
[{/->request->getQuery\s*\(\s*['"]([^'"]+)['"]/, "query"}, {/->request->getPost\s*\(\s*['"]([^'"]+)['"]/, "form"}, {/->request->getPut\s*\(\s*['"]([^'"]+)['"]/, "form"}, {/->request->getPatch\s*\(\s*['"]([^'"]+)['"]/, "form"}, {/->request->get\s*\(\s*['"]([^'"]+)['"]/, "query"}, {/->request->getHeader\s*\(\s*['"]([^'"]+)['"]/, "header"}, {/->cookies->get\s*\(\s*['"]([^'"]+)['"]/, "cookie"}, {/->dispatcher->getParam\s*\(\s*['"]([^'"]+)['"]/, "path"}] -
Request/response accessors a Phalcon handler (Micro closure, MVC controller action, or annotation-routed action) reads incoming data through.
$this->request/$this->cookiesare available in every one of those contexts — Phalcon binds Micro closures to the application instance, so$thisresolves the same way it does inside a controller action. -
PHALCON_MARKER_RE =
/Phalcon\\/ -
Every PHP analyzer is fed every
.phpfile in a project-wide scan, so this gate is the only thing keeping Phalcon off other frameworks' route/controller files. "Phalcon" itself is unambiguous — no other ecosystem framework spells this namespace — so a plain substring match is safe without the narroweruse X\Y;-only matching Laminas needs against the generic word "Zend". -
VERB_REGEX =
/\$(\w+)->(get|post|put|patch|delete|options|head)\s*\(\s*['"]([^'"\r\n]+)['"]\s*,/i -
- Micro app / Collection direct verb calls: $app->get('/path', function () {...}); $invoices->post('/add', 'add');
Class Method Summary
Instance Method Summary
- #analyze_file(path : String) : Array(Endpoint)
-
#tech : String
Instance-side view of the same declaration.
Class methods inherited from class Analyzer::Php::PhpEngine
test_path?(relative_path : String) : Bool
test_path?
Instance methods inherited from class FileScanEngine
analyze
analyze,
analyze_file(path : String) : Array(Endpoint)
analyze_file
Instance methods inherited from class Analyzer
analyze
analyze,
base_path : String
base_path,
base_paths : Array(String)
base_paths,
base_relative_path(path : String) : String
base_relative_path,
callees_needed? : Bool
callees_needed?,
content_matches?(content : String, markers : Regex) : Bool
content_matches?,
http_header_name(name : String) : String | Nil
http_header_name,
line_number_for_index(content : String, char_index : Int32) : Int32
line_number_for_index,
logger : NoirLogger
logger,
parallel_analyze(files : Array(String), &block : String -> Nil)
parallel_analyze,
read_file_content(path : String) : String
read_file_content,
result : Array(Endpoint)
result,
tech : String
tech,
unique_params(params : Array(Param)) : Array(Param)
unique_params,
url : String
url,
web_root_path(path : String, markers : Array(String)) : String
web_root_path
Constructor methods inherited from class Analyzer
new(options : Hash(String, YAML::Any))
new
Macros inherited from class Analyzer
analyzer_for(tech)
analyzer_for
Instance methods inherited from module FileHelper
all_files : Array(String)
all_files,
get_files_by_basename(basename : String) : Array(String)
get_files_by_basename,
get_files_by_extension(extension : String) : Array(String)
get_files_by_extension,
get_files_by_extensions(extensions : Array(String)) : Array(String)
get_files_by_extensions,
get_files_by_prefix(prefix : String) : Array(String)
get_files_by_prefix,
get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String)
get_files_by_prefix_and_extension,
get_files_by_relative_path(relative_path : String, root : String = "") : Array(String)
get_files_by_relative_path,
get_public_dir_files(base_path : String, folder : String) : Array(String)
get_public_dir_files,
get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String)
get_public_files,
walked_path(expanded : String) : String
walked_path
Class Method Detail
Instance Method Detail
Instance-side view of the same declaration. The per-file rescues live on
this base class, which has no way to name the analyzer that is running
inside them, so a skipped file could not be attributed to a tech.
Deriving it from analyzer_for keeps the name written exactly once.