B4 — declare the journal tables to Doctrine, which wanted to drop them - #274
Open
gplanchat wants to merge 2 commits into
Open
B4 — declare the journal tables to Doctrine, which wanted to drop them#274gplanchat wants to merge 2 commits into
gplanchat wants to merge 2 commits into
Conversation
… supprimer Les quatre tables du pont n'étaient déclarées à Doctrine par aucun `configureSchema` ni écouteur `postGenerateSchema` — alors qu'un docbloc de `DurableSchema` affirmait « branché aussi sur configureSchema côté bundle ». `doctrine:schema:update` et `doctrine:migrations:diff` construisent le schéma attendu depuis les seules entités, n'y trouvent pas ces tables, et les traitent en orphelines : la migration générée les supprime, avec le journal et toutes les exécutions en vol. `DurableSchema::configureSchema()` reprend la signature des adaptateurs amont qui résolvent le même problème — `DoctrineDbalAdapter`, `DoctrineTransport`, `DoctrineDbalStore` — y compris le paramètre de sonde `$isSameDatabase`, et saute par table celles que le schéma porte déjà. L'écouteur du bundle ne s'adosse volontairement pas à `AbstractSchemaListener` : `filterSchemaChanges()` n'existe pas sur toute la plage ^6.4 à ^8.0 que le bundle accepte, et un appel fatal sur la version basse coûterait plus que ce qu'il apporte. Il ne déclare donc que sur la connexion même que l'ORM inspecte — ne rien déclarer laisse l'exploitant gérer ce schéma, là où déclarer à tort créerait des tables dans la mauvaise base. Le paramètre de sonde reste offert à qui sait trancher. `dbal.auto_setup` accompagne le correctif : dès que les migrations tiennent le schéma, les deux mécanismes écriraient l'un derrière l'autre. Défaut `true`, donc rien ne change sans le demander. C'est aussi l'interrupteur que le transport Doctrine expose. `doctrine/orm` est déclaré — require-dev à la racine comme illuminate/*, suggest sur le bundle — plutôt que référencé en douce : l'écouteur ne s'enregistre que si la classe d'événement existe. Suite unit : 1079 tests contre 1073 sur main, mêmes 4 erreurs d'environnement. PHPStan signale localement trois class.notFound sur Doctrine\ORM, de même nature que les deux sur Illuminate\Cache déjà présents sur main : le paquet est déclaré, il n'est pas installé sur ce poste. Refs: B4 de documentation/audit/ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…oduction est testé La relecture croisée a montré que le correctif se refermait sur lui-même : l'écouteur passait `static fn(): bool => false` à `configureSchema()`, au motif écrit en docbloc que l'API d'`AbstractSchemaListener` aurait bougé entre Symfony ^6.4 et ^8.0. Vérification faite sur `v6.4.0`, `7.2` et `8.0`, `getIsSameDatabaseChecker()` y est déclarée à l'identique ; ce qui a bougé est `filterSchemaChanges()`, l'autre méthode. La justification étant fausse, la prudence qu'elle fondait ne l'était pas : dès que le journal a sa propre connexion — le cas que le README de cette branche décrit lui-même — plus rien n'était déclaré et `doctrine:migrations:diff` regénérait ses `DROP TABLE`. B4 était fermé pour le cas par défaut, rouvert pour son cas d'usage. La sonde est donc recopiée depuis `AbstractSchemaListener`, vingt lignes qui ne dépendent que de la DBAL, plutôt qu'héritée : elle y est `protected`, et l'étendre imposerait `symfony/doctrine-bridge` au bundle. `doctrine/orm` était écrit à la main dans `require-dev`, hors ordre alphabétique malgré `sort-packages`, et jamais résolu : les huit jobs qui font un `composer install` étaient rouges, aucun test n'avait tourné sur cette branche. La moitié `^2.19` de la contrainte était de toute façon insatisfiable ici — ORM 2 veut DBAL `^2.13.1 || ^3.2`, le pont veut `^3.7 || ^4.0` et le lock porte DBAL 4.4.4. Contrainte ramenée à `^3.0`, lock à jour. Le seul chemin de production n'avait aucun test : les cas existants appellent `configureSchema()` en lui passant la sonde, ce qui prouve la forme du schéma et jamais la décision de déclarer. `DurableSchemaListenerTest` exerce `postGenerateSchema()` avec la vraie sonde sur trois cas — même connexion, deux connexions sur le même fichier SQLite, deux bases distinctes. Le deuxième échoue sur le code d'avant ce commit (0 table déclarée au lieu de 4). Le README annonçait une abstention que le code ne pratique plus. Restent ouverts, hors périmètre de cette branche : le contournement de `schema_filter` (l'amont passe par `filterSchemaChanges()`, absente en 6.4), et l'absence de chemin de création supporté quand `auto_setup: false` — un `durable:setup`, à l'image de `messenger:setup-transports`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second audit finding.
doctrine:migrations:diffwas generatingDROP TABLEstatements against the journal.The defect
The four tables of the DBAL bridge were declared to Doctrine by no
configureSchemaand nopostGenerateSchemalistener.addToSchema()was only ever called byensure(), that is, by the bridge itself — verified, a single caller in the whole repository.A docblock claimed otherwise:
Consequence: Doctrine's tooling builds the expected schema from entities alone, does not find these tables, and treats them as orphans. The generated migration drops them — the journal, and with it every in-flight execution.
The fix
DurableSchema::configureSchema(Schema $schema, Connection $forConnection, \Closure $isSameDatabase)takes the signature of the three upstream adapters that solve exactly this problem —DoctrineDbalAdapter,DoctrineTransport,DoctrineDbalStore— database probe included. It skips, table by table, the ones the schema already carries.A listener in the bundle wires it to
postGenerateSchema, registered only if the ORM's event class exists: the DBAL bridge works without the ORM, and an application that only has DBAL has no schema to complete.auto_setupIt accompanies the fix rather than widening it: once migrations own the schema, the bridge's lazy creation writes behind their back. Defaults to
true, so nothing changes unless asked. This is the switch the Doctrine transport exposes for the same reason.Tests
Six cases, RED first (
Call to undefined method ...::configureSchema(),Unknown named parameter $autoSetup):auto_setup: falsecreates no table;auto_setupat its default still creates all four — regression net, this case already passed.Documentation
The bridge README carried "there is no migration to run" without saying what happened if the application did have one. It now carries the declaration, the connection boundary and
auto_setup. The docblock that lied is corrected.Addendum — 2026-09-04, after cross-review
The original text justified a deliberate caution: the listener did not lean on the upstream
AbstractSchemaListener, becausefilterSchemaChanges()"is recent and does not exist across the whole^6.4 || ^7.0 || ^8.0range". It therefore declared the tables only when the journal writes on the veryConnectionobject the ORM inspects, passingstatic fn(): bool => falseas the probe.The premise was wrong, and seven independent review axes reached it.
AbstractSchemaListener::getIsSameDatabaseChecker()is declared identically onv6.4.0,7.2and8.0— checked against the source. What moved isfilterSchemaChanges(), the other method. With the justification gone, so was the caution it funded: as soon as the journal has its own named connection — the case this branch's own README describes — nothing was declared anddoctrine:migrations:diffwent back to generating itsDROP TABLE. B4 was closed for the default case and silently reopened for the case it targeted.The probe is now copied from
AbstractSchemaListener— twenty lines that depend only on DBAL — rather than inherited: it isprotectedthere, and extending the class would imposesymfony/doctrine-bridgeon the bundle.Two more things the review found, both now fixed:
doctrine/ormhad been written intorequire-devby hand, out of alphabetical order despitesort-packages, and never resolved. The eight jobs that runcomposer installwere red: no test had ever run on this branch. The^2.19half of the constraint was unsatisfiable here anyway — ORM 2 wants DBAL^2.13.1 || ^3.2, the bridge wants^3.7 || ^4.0, and the lock carries DBAL 4.4.4. Constraint narrowed to^3.0, lock updated.configureSchema()and pass it the probe, which proves the shape of the schema and never the decision to declare.DurableSchemaListenerTestnow exercisespostGenerateSchema()with the real probe on three cases; the second one fails on the code that preceded this addendum.Still open, out of scope for this branch: the
schema_filterbypass (upstream goes throughfilterSchemaChanges(), absent in 6.4), and the lack of a supported creation path whenauto_setup: false— adurable:setup, along the lines ofmessenger:setup-transports.