Advice order depends on cache warmth: aspects register in reverse order when their classes are autoloaded through the weaving pipeline
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- php
- Área
- backend, documentation, testing
Línea de trabajo
Start with Container registration order, then trace advisor handling through AdvisorIndex, CachedAspectLoader, AdviceMatcher, and the compiled caches under src/Core/Cache/. Add a functional test for tied advices on cold and warm caches, remove the production_native_aspects workaround in tests/Functional/CacheWarmupIntoAnotherDirectoryTest.php, and update the advice ordering docs. Done means builds are byte-identical across cache modes, tied advice runs in aspect registration order, and the requested test, analysis, and performance commands pass.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Found while fixing #807 (PR #830).
Problem
Advices of the same kind and the same order value, coming from different aspects, are woven in a different order depending on how the aspect classes were loaded:
- Cold build, and every debug build: the aspect classes are autoloaded through the stream filter, and the aspects register in reverse registration order.
- Production kernel with a warm own cache: the aspect classes are loaded natively (skip list), and the aspects register in registration order.
Impact
- Runtime behaviour: the interceptor lists of tied advices are swapped. Their runtime execution order differs between a cold build and a warm rebuild or a
--cache-dirbuild, so the same release can run its advices in a different order depending on how its cache was produced. - Generated proxies differ between builds:
- the alias of colliding imports moves to the other aspect;
- the order of proxy methods changes;
- the key order of
_include.cachechanges.
- Builds are not reproducible: two builds of the same code are not byte-identical.
Reproduction (fixture project, production config, JIT off)
rm -rf var/cache/aspect, thenGO_AOP_CONFIGURATION=production bin/console cache:warmup:aop web/index.php. This warms the own cache.… --cache-dir=/tmp/warmrm -rf var/cache/aspect, then… --cache-dir=/tmp/colddiff -r -x _transformation.cache /tmp/cold /tmp/warmshows differences in:Main.phpSimpleEnum.phpBackedEnum.phpImportCollisionClass.php_include.cache(key order; ignore thestampline added by #816)
Debug builds, cold or warm, match the cold production build. Excluding src/Aspect/ from weaving makes all builds identical.
Cause chain
- On the first cache miss,
SourceTransformingLoader::getTransformers()callsContainer::getServicesByInterface(SourceTransformer::class). - Its
is_subclass_of($id, …)probe autoloads every pending factory id, and the aspects are pending factories. - An aspect missing from the cache goes through
AopComposerLoader, then thephp://filter, thentransformCode(), and back intogetTransformers(). The calls nest once per aspect. - At the innermost level, only the last-registered aspect is already loaded.
AdvisorIndex::getAdvisors()(reached throughConstructorExecutionTransformer::$sourceMarkers) loads and registers it first, and each outer level then adds its own aspect. - The advisors therefore end up in reverse registration order.
AbstractJoinpoint::sortAdvices()keeps the advisor order for ties, so tied advices from different aspects come out swapped. - With a warm own cache, the aspects are in the skip snapshot and load natively, so nothing nests and the order is the registration order.
A re-entry guard in getUnloadedAspects() alone does not help: the nesting enters through the container's probe for SourceTransformer.
Proposed decision for the implementing agent
Make the tie order explicit and independent of load timing (option c): sort advisors by the registration index of their aspect in the container, then by declaration order within one aspect, and only then let sortAdvices() apply the advice order.
- Why: load order becomes irrelevant. This covers every way the nesting can arise, including future ones, and the order is something users can rely on and read in the docs.
- Implementation sketch:
Containeralready keeps registrations in insertion order. Expose a stable registration index for a service id, preferably as an interface property or a small method on the internal contract, per the AGENTS rules.AdvisorIndex/CachedAspectLoaderrecord that index with each advisor.AdviceMatcher(or the code that builds interceptor lists) orders by it before the advice-order sort. Closure advisors registered without an aspect sort after aspects, in their own registration order.- Check the compiled advisor caches (
src/Core/Cache/): the stored order must be the deterministic one, so bump the format version if it changes.
- Optional follow-up (option a), as a separate issue: stop
Container::getServicesByInterface()from autoloading aspect ids while resolvingSourceTransformerservices, e.g. by resolving transformers by their registered tags or ids. This removes the nested weaving on cold builds, which is a performance and complexity win, but it is not needed for correctness once (c) is in. - Rejected: a re-entry guard only (option b). It handles today's path but leaves the order implicit.
Acceptance
- A cold build, a warm rebuild and a
--cache-dirbuild from a warm production cache are byte-identical, apart from_transformation.cacheand the include-map stamp. - Tied advices from different aspects run in aspect registration order in every mode. Add a functional test with two same-kind, same-order advices from two aspects, checked on a cold and on a warm cache.
- Remove the workaround in
tests/Functional/CacheWarmupIntoAnotherDirectoryTest.php: drop theproduction_native_aspectsfixture configuration and compare against the plainproductionconfiguration. - Document the tie order in the advice ordering docs.
composer test,composer analyze, andcomposer test:performance:transformation(the wiring changes). Run withopcache.jit=disable: the PHP 8.5.12 tracing-JIT bug breaks the functional subprocess tests.
Scoring
| Score | Why | |
|---|---|---|
| Effort | Medium | A registration-index contract on the container, carried through the advisor index and matcher, a possible advisor-cache format bump, and functional cold/warm tests. |
| Impact | Medium | It only affects applications with tied advices across aspects, but there it changes runtime behaviour between deploys, silently. |
- Lenguaje dominante
- PHP
- Estrellas
- 1.7k
- Forks
- 166
- Merge medio
- 1 d 1 h
- PR fusionados (30 d)
- 38
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de goaop/framework
-
Enhancement
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
cache:warmup:opcache runs out of memory with the default memory_limit (128M) on larger applicationsAbiertoBug
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
Los mantenedores suelen responder en 1 día
-
Bug To Be Discussed
Dificultad 5/5 Más de una semana Aptitud para principiantes 28/100
Los mantenedores suelen responder en 1 día
-
Bug
Dificultad 4/5 1-2 días Aptitud para principiantes 56/100
Los mantenedores suelen responder en 1 día
-
Enhancement Performance
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
Los mantenedores suelen responder en 1 día
Todos los issues de goaop/framework
Issues similares
-
enhancement priority Sales
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
novalo-dev/Prestashop-MCP-Server#201 ·
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
web-token/jwt-framework#757 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
Include flows spanning the full report time windowPosiblemente ocupada @somethingwithproof la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
Cacti/plugin_flowview#306 ·
Los mantenedores suelen responder en 1 día
-
bug origin: upstream priority: low
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
mattytap/Mattytap-Sermons#105 ·