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á.

verze: 1.0