Analýzy
NameResolver a Scope odpovídají na otázky, které si klade polovina nástrojů: co je tohle jméno doopravdy a v jaké funkci a třídě kód stojí.
Co je analýza
Strom říká, jak kód vypadá. Analýza je informace odvozená z celého souboru, kterou strom sám nenese: co znamená
jméno Order v tomhle souboru, jestli $this je k dispozici, které proměnné closure zachycuje.
V PhpSyntax je analýza obyčejná třída postavená nad FileNode; nic se neregistruje a žádné rozhraní se
neimplementuje.
$resolver = new PhpSyntax\Analyses\NameResolver($file);
$scope = new PhpSyntax\Analyses\Scope;
Analýza platí pro strom v okamžiku, kdy jste ji postavili. Po změně stromu si ji postavte znovu; v DressCode to dělá
engine sám a pravidlům ji podává přes RuleContext::getAnalysis().
NameResolver
Překládá jména podle jmenného prostoru a importů, s tím, co PHP dělá za běhu: nekvalifikovaná funkce nebo konstanta, která ve jmenném prostoru není, spadne do globálního.
$resolver->getNamespace($node); // 'App\Model', '' v globálním prostoru
$resolver->getClassImports($node); // alias => plné jméno, platné v místě uzlu
$resolver->getFunctionImports($node);
$resolver->getConstantImports($node);
$resolver->resolveClass($name); // 'App\Model\Order' z NameNode
$resolver->resolveFunction($name);
$resolver->resolveConstant($name);
$resolver->isGlobalFunctionCall($node, 'sizeof'); // volání globální funkce, případně té jedné
isGlobalFunctionCall() je otázka, kterou si dřív každý sniff řešil sám a většinou hůř: uzel musí být
volání funkce se jménem (ne $f() ani $obj->f()), jméno nesmí být klíčové slovo a po
překladu musí být globální. Tak se pozná, že count($a) uvnitř namespace App opravdu volá
count, a ne App\count, pokud taková funkce v souboru není importovaná.
Scope
Kde ve struktuře kód stojí:
$scope->getFunction($node); // FunctionNode, MethodNode, ClosureNode, ArrowFunctionNode, PropertyHookNode nebo null
$scope->getClass($node); // ClassLikeNode nebo null
$scope->hasThis($node); // je tu $this: v nestatické metodě, v closure, která ho zachytila
$scope->getCapturedVariables($closure); // proměnné z use (...)
hasThis() bere v úvahu i to, že closure $this zdědí, ale static function ne, a že
arrow funkce ho vidí vždy, když ho vidí okolí.
Vlastní analýza
Když víc pravidel potřebuje tutéž informaci, napište ji jako třídu s konstruktorem přijímajícím
FileNode (nebo bez parametrů). Uvnitř DressCode ji pravidlo dostane přes getAnalysis() a engine se
postará o její obnovu po každé změně stromu; mimo DressCode si ji stavíte sám. Třetí vestavěná analýza,
DressCode\Analyses\PhpDoc, patří už DressCode, protože stojí na phpstan/phpdoc-parseru a PhpSyntax
závislosti nemá.