---
suivi: 1072
date: 2026-07-30
sujet: Fix script diag Phase 0 foncier — closures + isolation sections
chantier: module-foncier
type: fix
statut: poussé
hash: 18eecdc8
fichiers:
  - tools/diag/foncier_phase0_prod.php
  - docs/suivi/SUIVI_1072_fix_script_diag_phase0.md
---

## PROMPT ENVOYÉ

SUIVI #1072 — tools/diag/foncier_phase0_prod.php (#1071) plante en prod au §4 :
Undefined variable $thin dans une closure (use ($ok, $absent) sans $thin).
Deux défauts : (A) supprimer TOUTES les closures → fonctions nommées + const ;
(B) isoler chaque section §1–§7 en try/catch Throwable visible [ALERTE] ;
garder §1–§3 à l’identique ; enrichir §4–§7 (lignes_financement, comptes
ventilés par enseigne, colonnes lots prescripteur/commission). Contrôles greps
closures vides, php -l, tableau fonctions. Un commit, push HECTAREG.

## SYNTHÈSE

Crash prod §4 : `$dumpCols = static function (...) use ($ok, $absent)` utilisait
`$thin` hors capture — récidive exacte #1037→#1038. Les §5–§7 n’ont jamais tourné.

Parade structurelle (#1072) :
- zéro closure dans le script ;
- helpers `diag_ok` / `diag_absent` / `diag_alerte` / `diag_section_catch` /
  `diag_dump_cols` / `diag_lots_prescripteur_commission_cols` /
  `diag_count_table` / `diag_count_by_enseigne` ;
- const `DIAG_SEP` / `DIAG_THIN` ;
- chaque section §1–§7 wrappée try/catch : message BRUT + fichier:ligne + trace,
  puis poursuite ;
- §2 spatial inchangé (validé MySQL 8.4.10 en prod) ;
- §4 ajoute `lignes_financement` + scan lots.*prescripteur*/commission* ;
- §6 ventile `rubriques_bilan` et `postes_budgetaires_types` par enseigne.

### Tableau fonctions nommées (remplace le contrôle closure ↔ use)

| nom | paramètres | variables utilisées dans le corps | toutes déclarées ? |
|-----|------------|-----------------------------------|--------------------|
| diag_ok | string $msg | $msg | OUI |
| diag_absent | string $msg | $msg | OUI |
| diag_alerte | string $msg | $msg | OUI |
| diag_section_catch | string $section, Throwable $e | $section, $e (+ appels diag_alerte) | OUI |
| diag_dump_cols | string $table | $table, $col (foreach), Schema, DIAG_THIN | OUI |
| diag_lots_prescripteur_commission_cols | (aucun) | $found, $col, $lower, Schema, DIAG_THIN | OUI |
| diag_count_table | string $table | $table, $n, Schema, DB | OUI |
| diag_count_by_enseigne | string $table | $table, $rows, $row, $code, $nom, Schema, DB | OUI |

### Contrôles bruts

```
rg -n "function \(" tools/diag/foncier_phase0_prod.php
(vide)

rg -n "use \(" tools/diag/foncier_phase0_prod.php
(vide)

php -l tools/diag/foncier_phase0_prod.php
No syntax errors detected in tools/diag/foncier_phase0_prod.php

head -3 : <?php + docblock #1071/#1072
wc -l : 430 lignes
git status : M tools/diag/... + ?? SUIVI_1072 (puis commités) ; aucun app/database/js/routes
```

## DÉPLOIEMENT / TEST

1. `git pull origin main`
2. Aucune migrate / rebuild
3. `php tools/diag/foncier_phase0_prod.php`
4. Vérifier que §4 à §7 s’affichent même si une section intermédiaire échoue
5. Coller §4 (colonnes + lignes_financement + lots), §5 (SHOW INDEX #920),
   §6 (totaux + ventilation ENVOL vs HECTARE), §7 (libelle/nom) dans le ticket

## LEÇON

La règle #1038 (« tableau closure ↔ use ↔ variables libres ») n’a pas été
appliquée au #1071 : le script a été écrit avec le même pattern confortable
`$ok = static function ... use (...)` que les diag antérieurs, et le contrôle
humain s’est limité à `php -l` (qui ne voit pas les variables libres d’une
closure). Le `$thin` oublié dans le `use` du helper `$dumpCols` — ajouté en
fin de rédaction — n’a été exercé qu’en prod, après les §1–§3 réussis.

Ce que change la suppression des closures : on élimine la classe d’erreur
entière (plus de liste `use` à maintenir), on remplace la discipline fragile
« vérifier chaque use » par une structure où les helpers sont des fonctions
nommées à portée globale et des `const` de fichier. L’isolation try/catch par
section évite qu’un second bug du même type (ou autre) n’emporte le reste du
diagnostic — un script de preuve doit livrer le maximum d’information même
partiellement en échec, jamais des zéros silencieux.
