Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Performance: skip parsing files that no advisor or node rewriter can affect (~90% of files on a cold cache)

Open
#747 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
php
Domain
performance

Research direction

Start in plan mode, as the issue requires an approved design before implementation. Read src/Instrument/Transformer/StreamMetaData.php, SyntaxTreeRewriter, WeavingTransformer, and the pointcut and cache-map APIs; first settle where the pre-check belongs and how unknown match cases and custom transformers veto skipping. Done means tests show unchanged woven output and cache maps against full parsing, with warmup and composer test:performance:transformation benchmarks before and after.

Written by the indexing model from the issue text.

Description

Enhancement Performance

Implementation note: start in plan mode. The pointcut-side API and the fallback rules need an approved design before any code is written.

Problem

On a cache miss, StreamMetaData always parses the full file with php-parser plus a NameResolver pass (src/Instrument/Transformer/StreamMetaData.php:134), before any transformer can decide it has nothing to do.

SyntaxTreeRewriter already skips its tree walk when no rule marker is present (#736). The parse itself is never skipped, and WeavingTransformer then reflects every class against every advisor. In typical setups the pointcuts select classes by namespace or name, so most files are parsed for nothing.

Measurement

Cold cache:warmup:aop over vendor/ (3,935 files, 14 MB of source). Two advisors: execution(public Symfony\Component\Console\Command\**->*(*)) and execution(public Doctrine\ORM\**->*(*)). PHP 8.5, JIT off.

phase time
StreamMetaData (parse + NameResolver) 6.2 s (67%)
WeavingTransformer 2.3 s
SyntaxTreeRewriter 0.07 s
cache writes + flush 0.16 s
  • Only 393 of 3,935 files (10%) were transformed.
  • Tokenizing all files with PhpToken::tokenize() takes 0.2 s, against about 4 s for the parse alone.

Proposal (to be refined in plan mode)

Before the parse, do a cheap pre-check on the raw source:

  1. Tokenize, or use another cheap scan, to extract the namespaces and the declared class, trait and enum names (and function names when INTERCEPT_FUNCTIONS is on).
  2. Ask each advisor whether it could match the declared names. Add an internal, optional capability on pointcuts, for example "decide by name only", which returns yes, no or unknown.
    • Name-based class filters and namespace filters answer definitively.
    • Inheritance (Foo+), attribute filters (@within, @execution), ! combinations and custom pointcuts answer "unknown", which forces a full parse.
  3. Skip the parse only when no advisor can match and no PrefilteredNodeRewriter marker is present (the same check SyntaxTreeRewriter does today). In that case, record the file as untransformed, including its class names for the skip map (CachePathManager::registerClassForResource()), and serve the source as is.

Open questions for the plan

  • Where the pre-check lives: SourceTransformingLoader, a new first stage, or a SourceTransformer capability. Custom transformers registered through registerTransformerServices() must be able to opt out of, or veto, the skip.
  • Exact semantics of "could match" for ||, && and !, and for within(**)-style wildcards.
  • Introductions and INTERCEPT_INITIALIZATIONS / INTERCEPT_INCLUDES markers.
  • Interaction with aspects that are first loaded on the miss path (getUnloadedAspects()).

Acceptance

  • No change in the woven output and no change in the cache maps compared with full parsing. A test should prove this over the fixture project and a larger corpus.
  • Warmup benchmark before and after.
  • composer test:performance:transformation before and after.

Estimate

Cold warmup about 9 s → 3–4 s (estimated) for configurations with name-based pointcuts. No gain, and no regression, for configurations with inheritance or attribute pointcuts. Medium to high risk, large change.

Dominant language
PHP
Stars
1.7k
Forks
166
Avg merge
11h 20m
Merged PRs (30d)
37

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from goaop/framework

All issues in goaop/framework

Similar issues

More PHP issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.