Skip to content

fix(proxy): emit fully-qualified parent for classes extending global classes - #762

Merged
lisachenko merged 1 commit into
masterfrom
fix/759-global-parent-fqn
Oct 9, 2026
Merged

lisachenko merged 1 commit into
masterfrom
fix/759-global-parent-fqn

Conversation

@lisachenko

Copy link
Copy Markdown
Member

What and why

Fixes #759.

The bug: a woven class in a namespace that extends a global class, for example class Coll extends \ArrayObject in namespace Bug2, got extends ArrayObject in its proxy. That resolved to Bug2\ArrayObject, and including the proxy failed with Error: Class "Bug2\ArrayObject" not found.

The change:

  • Parent class: ClassProxyGenerator roots the parent class name it takes from reflection: '\\' . $parentClass->getName(). This mirrors what was already done for the original interfaces.
  • Introduced traits and interfaces: also always rooted, through a new rootClassName() helper. Callers that skip AdviceMatcher (which already roots them) are now safe too.
  • Trait aliases: ClassGenerator::addTraitAlias() keeps the trait name as given instead of stripping the leading \. Before, an aliased global trait was written as GlobalTrait::m in the alias line, which resolved inside the proxy's namespace.
  • What stays the same: ClassGenerator::classNameNode() keeps its semantics (only its docblock is extended). The FooOriginalTrait reference keeps its deliberate short relative name, and the short use aliases for framework and aspect classes are unchanged.
  • Docs: one line in src/Proxy/AGENTS.md.

Verification

The repro from #759 (Bug2\Coll extends \ArrayObject with an advised hello(), empty cache):

before after
Output Fatal error: Uncaught Error: Class "Bug2\ArrayObject" not found [advice] coll
Proxy declaration class Coll extends ArrayObject implements \IteratorAggregate, … class Coll extends \ArrayObject implements \IteratorAggregate, …

Tests

  • Snapshots: new fixture global-parent-class.php (Test\ns1\GlobalParentCollection extends \ArrayObject implements \Countable) with woven and proxy snapshots. WeavingTransformerTest::testWeaverRootsGlobalParentClassOfNamespacedClass compares against them and asserts the proxy compiles.
  • Runtime load: testWovenClassWithGlobalParentClassWorksAtRuntime includes the woven trait and proxy from vfs, then checks is_subclass_of(\ArrayObject), instantiation and item access.
  • ClassProxyGeneratorTest:
    • a global parent is emitted as extends \ArrayObject, while the OriginalTrait use stays short;
    • introduced global traits and interfaces are rooted, whether they are passed with or without a leading \.
  • ClassGeneratorTest: the alias of an explicitly rooted global trait stays fully qualified.
  • Existing snapshots: none changed.
  • Gates, run locally: full phpunit (3095 tests), phpstan level 10 and php-cs-fixer are green. Only src/Proxy is touched, so the transformation performance group does not apply.

No CHANGELOG entry: the bug comes from the trait-based engine of the unreleased 4.0.

Checklist

  • The title follows Conventional Commits (type(scope): summary)
  • Tests cover the change
  • composer check passes
  • User-facing or breaking changes are in CHANGELOG.md (and the upgrade guide for breaking changes)

🤖 Generated with Claude Code

https://claude.ai/code/session_01KXjD75egSqDNujmNS5kr5c


Generated by Claude Code

…classes

A woven namespaced class extending a global class (\ArrayObject,
\Exception, ...) got a proxy with `extends ArrayObject`: the reflection
parent name has no leading backslash, so ClassGenerator emitted a
relative name that resolved in the proxy namespace and loading the proxy
failed with `Class "Ns\ArrayObject" not found`.

- ClassProxyGenerator roots the reflection parent name, like it already
  does for the original interfaces; introduced traits and interfaces are
  rooted too, so direct callers that bypass AdviceMatcher are safe.
- ClassGenerator::addTraitAlias() keeps the trait name as given, so a
  rooted global trait stays fully qualified in its alias adaptation.
- The OriginalTrait reference keeps its deliberate short relative name.

Tests: weaving snapshot plus a runtime load check for a namespaced class
extending \ArrayObject, ClassProxyGenerator cases for the global parent
and global introductions, and a ClassGenerator alias case. No existing
snapshot changed.

Closes #759

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KXjD75egSqDNujmNS5kr5c
@codecov

codecov Bot commented Oct 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.64%. Comparing base (4e95fdd) to head (1110746).

Additional details and impacted files
@@            Coverage Diff            @@
##             master     #762   +/-   ##
=========================================
  Coverage     97.63%   97.64%           
- Complexity     1829     1830    +1     
=========================================
  Files           104      104           
  Lines          4829     4831    +2     
=========================================
+ Hits           4715     4717    +2     
  Misses          114      114           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lisachenko
lisachenko marked this pull request as ready for review October 9, 2026 22:22
@lisachenko
lisachenko merged commit d99f545 into master Oct 9, 2026
14 checks passed
@lisachenko
lisachenko deleted the fix/759-global-parent-fqn branch October 9, 2026 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Proxy of a namespaced class extending a global class references the parent unqualified (Class "Ns\ArrayObject" not found)

2 participants