Pāriet uz saturu

AST meklēšana

Greph AST režīms meklē PHP pirmkodu pēc struktūras, nevis teksta. Raksti tiek veidoti kā parasts PHP kods ar metavariabliem $VARIABLE un $$$VARIADIC. Rakstu parsē, izmantojot nikic/php-parser, iegūstot tādu pašu AST struktūru kā meklētajam pirmkodam, un pēc tam salīdzina pa mezgliem.

Tas ir tāds pats modelis kā ast-grep, un sg saderības iesaiņotājs nodrošina ast-grep CLI saskarni virs tā paša dzinēja.

Īsi piemēri

Apvalks
# Find every constructor call
./vendor/bin/greph -p 'new $CLASS()' src

# Find every method call with any number of arguments
./vendor/bin/greph -p '$obj->$method($$$ARGS)' src

# Find old-style array() calls
./vendor/bin/greph -p 'array($$$ITEMS)' src

# Find void-returning functions
./vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src

# Find isset ternaries that should be ??
./vendor/bin/greph -p 'isset($x) ? $x : $default' src

Rakstu sintakse

Raksts ir derīgs PHP pirmkods. Jebkurā vietā, kur varētu atrasties īsta izteiksme vai priekšraksts, tā mezgla uztveršanai var ievietot metavariablu.

Sintakse Atbilstība Piemērs
$VAR Jebkurš atsevišķs AST mezgls, kas tiek uztverts kā VAR $x + $y atbilst foo() + bar
$_ Jebkurš atsevišķs AST mezgls, kas netiek uztverts $_->method() atbilst jebkuram saņēmējam
$$$ARGS Nulle vai vairāk mezglu (variantiski), kas tiek uztverti kā ARGS func($$$ARGS) atbilst func(), func(1), func(1, 2, 3)
$VAR (atkārtots) Tas pats $VAR abās vietās jāatbilst vienādai struktūrai $x == $x atbilst a == a, bet ne a == b

Metavariablas nosaukumā pēc vienošanās izmanto lielos burtus, taču tā nav obligāta prasība. $_ ir universāls aizstājējzīmes elements, kas netiek uztverts.

$$$VARIADIC darbojas visur, kur PHP pieļauj mezglu sarakstu: funkciju argumentos, masīva elementos, parametru sarakstos un priekšrakstu blokos. Tvērums saglabā visu atbilstošo secību, arī tad, ja tajā nav neviena elementa.

Identifikatoru metavariabli

Klašu, funkciju, saskarņu, trašu un uzskaitījumu nosaukumi ir identifikatori, nevis izteiksmes, tāpēc PHP parasti nepieņemtu $Name pēc class. Greph pirms parsēšanas pārveido šos rakstus, lai metavariabla joprojām darbotos:

Apvalks
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src

Tāda pati pārveide notiek attiecībā uz class, interface, trait, enum un function.

Atkārtotas metavariabli

Metavariablas atkārtota izmantošana vienā rakstā nozīmē, ka abiem gadījumiem strukturāli jāatbilst tam pašam mezglam. Tas noder lieka koda atrašanai:

Apvalks
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src

# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src

Tas pats noteikums attiecas uz $$$VARIADIC: diviem $$$ARGS gadījumiem jāatbilst vienādai secībai.

Failu filtrēšana

AST režīms izmanto to pašu failu koka pārlūku, ko teksta režīms, un pēc noklusējuma lieto failu tipa filtru php. To var mainīt ar --type, --type-not, --glob, --no-ignore un --hidden. Pārlūks joprojām ievēro .gitignore un .grephignore, kā arī izlaiž bināros failus.

Apvalks
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src

Izvade

Noklusējuma izvadē tiek izmantots tāds pats formāts file:line:content kā teksta režīmā, bet atbilstošais kods ir apvienots vienā rindā:

Text
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();

Nododiet --json, lai izvadītu strukturētas atbilstības:

JSON
[
  {
    "file": "src/Greph.php",
    "start_line": 38,
    "end_line": 38,
    "start_file_pos": 1234,
    "end_file_pos": 1252,
    "code": "new FileWalker()"
  }
]

Parsēšanas kļūdas

Pēc noklusējuma Greph klusi izlaiž failus, kurus neizdodas parsēt. Tā ir piemērota rīcība, strādājot ar lielu koda bāzi, kurā ir eksperimentāls vai ģenerēts PHP. Nododiet --strict-parse (komandām greph-index ast-index search un greph-index ast-cache search), lai kļūdas vietā tiktu parādīta parsēšanas kļūda, vai, tieši izmantojot fasādi, iestatiet skipParseErrors: false laukā AstSearchOptions.

Izmantošana programmā

PHP
use Greph\Greph;
use Greph\Ast\AstSearchOptions;

$matches = Greph::searchAst(
    '$obj->$method($$$ARGS)',
    'src',
    new AstSearchOptions(jobs: 4),
);

foreach ($matches as $match) {
    echo "{$match->file}:{$match->startLine}\n";
    echo $match->code . "\n";

    foreach ($match->captures as $name => $node) {
        echo "  {$name} = " . get_debug_type($node) . "\n";
    }
}

$match->node ir uztvertais PhpParser\Node (tāpēc to var pārlūkot ar PHP-Parser NodeVisitor), bet $match->captures katru metavariablas nosaukumu sasaista ar uztverto mezglu vai mezglu sarakstu.

Kā tas darbojas

Apstrādes konveijera darbības:

  1. Raksta parsēšana: Greph\Ast\PatternParser pārveido identifikatoru metavariablas, pēc tam parsē rakstu ar PHP-Parser.
  2. Avota parsēšana: katrs kandidātfails tiek parsēts vienreiz. ast-cache režīmā šo darbību aizstāj deserializēšana no kešatmiņas krātuves.
  3. Kandidātu filtrēšana: Greph\Ast\AstPatternPrefilter izvelk no raksta aptuvenu parakstu (izsaukuma nosaukumu, klases nosaukumu, priekšraksta tipu) un izmanto to, lai izlaistu failus, kuros acīmredzami nevar būt atbilstības. ast-index režīmā šajā darbībā tiek vaicāta iepriekš aprēķināta faktu krātuve.
  4. Salīdzināšana: Greph\Ast\PatternMatcher pārlūko avota AST un salīdzina to ar raksta AST, procesā sasaistot metavariablas un nodrošinot, ka atkārtotās variablēs ir vienādas vērtības.
  5. Rezultātu izvadīšana: atbilstošie mezgli tiek pārveidoti par AstMatch objektiem ar rindu un baitu pozīcijām un sākotnējā pirmkoda fragmentu.

Atkārtotiem darbiem indeksētā AST meklēšana un kešotā AST meklēšana izlaiž lielāko daļu 2. un 3. darbības.