Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ var/
site/
docs/
*.md

# Secrets de production : montés par FrankenDeploy depuis le serveur
.env.local
.env.*.local
*.sql.gz
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@

**Présentation des versions du framework Edu Framework**

## Non publié
## v2.9.0 - 21/10/2026

**new features**

- Profiler : **flamegraph intégré** à l'onglet **Code**, sans fichier à télécharger. Trois vues s'ajoutent au tableau des fonctions : **Flamegraph** (appels regroupés, le plus coûteux à gauche, `#code/{token}/flame`), **Chronologique** (ordre des appels avec axe du temps, `#code/{token}/time`) et **Comparer** (flamegraph différentiel de deux requêtes profilées, en rouge ce qui est plus lent et en bleu ce qui est plus rapide, `#code/{token}/diff/{autre}`). Les couleurs indiquent l'origine du code (`app` / `framework` / `vendor`). On peut zoomer d'un clic, remonter par le chemin d'appels ou avec Échap, rechercher une fonction (avec le temps total trouvé), afficher une infobulle et le détail de la fonction, avec un lien vers ses appelants et ses appelés. Les appels de moins de 0,1 % sont regroupés dans « … (autres) »
- Barre de debug : **mini-flamegraph** dans le panneau du bloc **Code** ; un clic l'ouvre en grand dans le profiler
- Endpoint `GET /edu-code-profile` : nouveau `format=flame` avec les paramètres `order` (`heavy` / `time`), `compare` (token de référence), `min` (élagage) et `depth` (profondeur) ; nouvelles méthodes `CodeProfileAggregator::flameTree()` et `CodeProfileAggregator::diffTree()`
- Déploiement sur un serveur Debian / Ubuntu avec [FrankenDeploy](https://yoanbernabeu.github.io/frankendeploy/) : nouvelle commande `php bin/edu make:deploy` qui génère le fichier `frankendeploy.yaml` (options `--domain`, `--name`, `--database=mariadb|mysql|sqlite|none`, `--force`) avec les dossiers partagés entre les déploiements (`.env.local`, `var/logs`, `var/sessions`, `var/sqlite`, `public/upload`)
- `Dockerfile` multi-stage : `frankenphp_base` (socle commun), `frankenphp_prod` (image de production : HTTP sur le port 8080, utilisateur non-root `app`, `php.ini-production`, OPcache sans revalidation, sessions dans `var/sessions`, sans Excimer ni dépendances de dev, healthcheck) et `frankenphp_dev` (image de développement, ciblée explicitement par `compose.yaml`). Nouveau script `docker-entrypoint.sh` qui attend la base de données avant de démarrer FrankenPHP
- Variables d'environnement : nouvelle méthode `ConfigCore::loadEnv()` utilisée par l'application et par `bin/edu`. Les fichiers `.env` et `.env.local` sont optionnels (le `.env.local` est prioritaire sur le `.env`), les variables de l'environnement du processus (Docker) sont prioritaires et lues par `ConfigCore::getEnv()` / `existEnv()` même lorsque `variables_order` ne contient pas `E` (cas de `php.ini-production`). Si `DATABASE_URL` est définie (`mysql://user:pass@host:3306/db`), les variables `DB_*` manquantes en sont déduites ; une `DATABASE_URL` MySQL / MariaDB / PostgreSQL mal formée (Exemple: mot de passe contenant `#` non encodé) lève une `ErrorConfigException` explicite

**bug Fixes**

- `ConfigCore::getEnv()` ne lève plus d'avertissement pour une variable absente (retourne une chaîne vide)
- `bin/edu` démarre sans fichier `.env`
- Tests : `APP_ENV` hérité du shell ou du conteneur de développement n'influence plus les tests (`tests/bootstrap.php`)

**documentation**

- update [La barre de debug et le profiler](docs/boost/debug-bar.md) : nouvelles sections « Le flamegraph intégré » et « Comparer deux requêtes » (avec captures d'écran), mini-flamegraph de la barre de debug, paramètres `format=flame`, `order`, `compare`, `min` et `depth` de l'API
- new [Déployer son application](docs/deploy/index.md) : préparation du serveur, `make:deploy`, variables d'environnement, déploiement, import de la base, dossiers partagés, commandes du quotidien, image de production
- update [La commande bin/edu](docs/installation/command-edu.md) : commande `make:deploy`
- update [L'application dans Docker](docs/installation/docker.md) : lien vers l'image de production

## v2.8.0 - 20/10/2026

Expand Down
100 changes: 92 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,22 @@
# https://frankenphp.dev/docs/docker/
#
# Edu-Framework exige PHP >= 8.4 (voir composer.json et public/index.php)
FROM dunglas/frankenphp:php8.4
#
# Image multi-stage :
# frankenphp_base -> socle commun (extensions PHP, Composer)
# frankenphp_prod -> image de production (déploiement avec FrankenDeploy,
# voir frankendeploy.yaml et la doc "Déployer son application")
# frankenphp_dev -> image de développement (compose.yaml), stage par défaut
#
# docker build --target frankenphp_prod -t mon-app . => image de production
# docker compose build => image de développement

# Outils CLI : curl (tests d'API, comme pour le poste de travail local)
###################
# Socle commun
###################
FROM dunglas/frankenphp:php8.4 AS frankenphp_base

# Outils CLI : curl (tests d'API et healthcheck de l'image de production)
# et unzip (décompression propre des archives Composer)
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl unzip \
Expand Down Expand Up @@ -32,21 +45,92 @@ RUN install-php-extensions \
intl \
opcache

# Composer (depuis l'image officielle)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app

ENV COMPOSER_ALLOW_SUPERUSER=1

###################
# Production
###################
# Contrat FrankenDeploy : stage nommé frankenphp_prod, HTTP sur le port 8080
# (le HTTPS est assuré par le Caddy frontal du serveur), utilisateur non-root
# "app" (uid 1000) et script docker-entrypoint
FROM frankenphp_base AS frankenphp_prod

ENV APP_ENV=prod
# HTTP simple sur le port 8080 (voir Caddyfile)
ENV SERVER_NAME=:8080

# Configuration PHP de production
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# - OPcache : le code ne change pas sans redéploiement (pas de revalidation)
# - Sessions : stockées dans var/sessions, dossier partagé entre les releases
# (les utilisateurs restent connectés après un déploiement)
COPY <<EOF $PHP_INI_DIR/conf.d/app.prod.ini
expose_php = Off
opcache.validate_timestamps = 0
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.interned_strings_buffer = 16
realpath_cache_size = 4096K
realpath_cache_ttl = 600
session.save_path = /app/var/sessions
EOF

# Utilisateur non-root qui exécute l'application
ARG UID=1000
ARG GID=1000
RUN groupadd --gid ${GID} app \
&& useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash app

# Dépendances PHP sans les outils de développement (couche mise en cache tant
# que composer.json / composer.lock ne changent pas)
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-autoloader --no-scripts --no-interaction --no-progress --prefer-dist

# Sources de l'application (le .dockerignore exclut .env, var/, vendor/, docs...)
COPY --chown=${UID}:${GID} . ./
RUN composer dump-autoload --optimize --no-dev

# Configuration du serveur
COPY Caddyfile /etc/frankenphp/Caddyfile

# Dossiers d'exécution accessibles en écriture par l'utilisateur app
# (var/logs, var/sessions, var/sqlite et public/upload sont des dossiers
# partagés montés par FrankenDeploy, voir frankendeploy.yaml)
RUN mkdir -p var/cache var/logs var/sessions var/sqlite public/upload \
&& chown -R app:app var public/upload /data /config

# Attend la base de données puis démarre FrankenPHP
COPY --chmod=755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint
ENTRYPOINT ["docker-entrypoint"]
CMD ["--config", "/etc/frankenphp/Caddyfile", "--adapter", "caddyfile"]

# Sonde applicative : vérifie que l'application répond, pas seulement Caddy
# (même URL que healthcheck_path dans frankendeploy.yaml)
HEALTHCHECK --start-period=60s CMD curl -f http://localhost:8080/ || exit 1

USER app

EXPOSE 8080

###################
# Développement (stage par défaut, utilisé par compose.yaml)
###################
FROM frankenphp_base AS frankenphp_dev

# Profiler de code de la barre de debug (Excimer, profiler par échantillonnage)
# Installé uniquement pour l'image de développement :
# docker compose build --build-arg EDU_PROFILER=0 => image sans profiler
# Doc : https://www.mediawiki.org/wiki/Excimer
ARG EDU_PROFILER=1
RUN if [ "$EDU_PROFILER" = "1" ]; then install-php-extensions excimer; fi

# Composer (depuis l'image officielle)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app

# Installation des dépendances PHP (couche mise en cache tant que
# composer.json / composer.lock ne changent pas)
ENV COMPOSER_ALLOW_SUPERUSER=1
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress

Expand Down
2 changes: 2 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ services:
container_name: ${APP_NAME}-http-server
build:
context: .
# Stage de développement du Dockerfile (frankenphp_prod sert au déploiement)
target: frankenphp_dev
args:
# Profiler de code de la barre de debug (extension Excimer) :
# 1 => installé (défaut, développement) | 0 => image sans profiler
Expand Down
70 changes: 70 additions & 0 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/sh
# Edu-Framework - point d'entrée de l'image de production (stage frankenphp_prod)
#
# Attend que la base de données accepte les connexions, puis démarre FrankenPHP.
# La base est décrite par DATABASE_URL (base gérée par FrankenDeploy) ou par les
# variables DB_* (DB_HOST_STATUS=true), passées par Docker ou le fichier .env.local.
set -e

log() {
echo "[Edu-Framework] $1"
}

# Hôte et port de la base de données, calculés par le framework lui-même
# (même lecture des variables que l'application : environnement, .env.local, .env)
db_target() {
# Code PHP entre guillemets simples : les $ ne doivent pas être interprétés par le shell
# shellcheck disable=SC2016
php -r '
require "/app/vendor/autoload.php";
use Studoo\EduFramework\Core\ConfigCore;
new ConfigCore([]);
try {
ConfigCore::loadEnv("/app/");
} catch (Throwable $exception) {
// Configuration invalide (Exemple: DATABASE_URL mal formée) : arrêt explicite
fwrite(STDERR, $exception->getMessage() . PHP_EOL);
exit(1);
}
if (ConfigCore::getEnv("DB_HOST_STATUS") !== "true"
|| in_array(ConfigCore::getEnv("DB_TYPE"), ["mysql", "mariadb", "pgsql"], true) === false
|| ConfigCore::getEnv("DB_HOST") === "") {
exit;
}
$port = ConfigCore::getEnv("DB_SOCKET");
if ($port === "") {
$port = ConfigCore::getEnv("DB_TYPE") === "pgsql" ? "5432" : "3306";
}
echo ConfigCore::getEnv("DB_HOST") . " " . $port;
'
}

wait_for_database() {
if ! target=$(db_target); then
log "Configuration invalide : vérifiez les variables d'environnement (frankendeploy env list prod)"
exit 1
fi
if [ -z "$target" ]; then
return 0
fi

host=${target% *}
port=${target#* }
log "Attente de la base de données $host:$port..."
attempts=0
# shellcheck disable=SC2016
until php -r 'exit(@fsockopen($argv[1], (int) $argv[2], $e, $s, 2) ? 0 : 1);' "$host" "$port"; do
attempts=$((attempts + 1))
if [ "$attempts" -ge 60 ]; then
log "Base de données injoignable"
exit 1
fi
sleep 1
done
log "Base de données prête"
}

wait_for_database

log "Démarrage de FrankenPHP"
exec docker-php-entrypoint "$@"
2 changes: 2 additions & 0 deletions docs/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
* [Route - Générer une URL par le nom](boost/route.md)
* [Les erreurs HTTP](boost/http-error.md)
* [La barre de debug et le profiler](boost/debug-bar.md)
* [Déployer](deploy/index.md)
* [Déployer son application](deploy/index.md)
* [Migrer](migrate/major.md)
* [Migrer de la version 2.3 à la version 2.8](migrate/migration-2_3-2_8.md)
* [Migrer de la version 2.2 à la version 2.3](migrate/migration-2_2-2_3.md)
Expand Down
14 changes: 13 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@

**Présentation des versions du framework Edu Framework**

## Non publié
## v2.9.0 - 21/10/2026

**new features**

- Profiler : **flamegraph intégré** à l'onglet **Code**, sans fichier à télécharger. Trois vues s'ajoutent au tableau des fonctions : **Flamegraph** (appels regroupés, le plus coûteux à gauche, `#code/{token}/flame`), **Chronologique** (ordre des appels avec axe du temps, `#code/{token}/time`) et **Comparer** (flamegraph différentiel de deux requêtes profilées, en rouge ce qui est plus lent et en bleu ce qui est plus rapide, `#code/{token}/diff/{autre}`). Les couleurs indiquent l'origine du code (`app` / `framework` / `vendor`). On peut zoomer d'un clic, remonter par le chemin d'appels ou avec Échap, rechercher une fonction (avec le temps total trouvé), afficher une infobulle et le détail de la fonction, avec un lien vers ses appelants et ses appelés. Les appels de moins de 0,1 % sont regroupés dans « … (autres) »
- Barre de debug : **mini-flamegraph** dans le panneau du bloc **Code** ; un clic l'ouvre en grand dans le profiler
- Endpoint `GET /edu-code-profile` : nouveau `format=flame` avec les paramètres `order` (`heavy` / `time`), `compare` (token de référence), `min` (élagage) et `depth` (profondeur) ; nouvelles méthodes `CodeProfileAggregator::flameTree()` et `CodeProfileAggregator::diffTree()`
- Déploiement sur un serveur Debian / Ubuntu avec [FrankenDeploy](https://yoanbernabeu.github.io/frankendeploy/) : nouvelle commande `php bin/edu make:deploy` qui génère le fichier `frankendeploy.yaml` (options `--domain`, `--name`, `--database=mariadb|mysql|sqlite|none`, `--force`) avec les dossiers partagés entre les déploiements (`.env.local`, `var/logs`, `var/sessions`, `var/sqlite`, `public/upload`)
- `Dockerfile` multi-stage : `frankenphp_base` (socle commun), `frankenphp_prod` (image de production : HTTP sur le port 8080, utilisateur non-root `app`, `php.ini-production`, OPcache sans revalidation, sessions dans `var/sessions`, sans Excimer ni dépendances de dev, healthcheck) et `frankenphp_dev` (image de développement, ciblée explicitement par `compose.yaml`). Nouveau script `docker-entrypoint.sh` qui attend la base de données avant de démarrer FrankenPHP
- Variables d'environnement : nouvelle méthode `ConfigCore::loadEnv()` utilisée par l'application et par `bin/edu`. Les fichiers `.env` et `.env.local` sont optionnels (le `.env.local` est prioritaire sur le `.env`), les variables de l'environnement du processus (Docker) sont prioritaires et lues par `ConfigCore::getEnv()` / `existEnv()` même lorsque `variables_order` ne contient pas `E` (cas de `php.ini-production`). Si `DATABASE_URL` est définie (`mysql://user:pass@host:3306/db`), les variables `DB_*` manquantes en sont déduites ; une `DATABASE_URL` MySQL / MariaDB / PostgreSQL mal formée (Exemple: mot de passe contenant `#` non encodé) lève une `ErrorConfigException` explicite

**bug Fixes**

- `ConfigCore::getEnv()` ne lève plus d'avertissement pour une variable absente (retourne une chaîne vide)
- `bin/edu` démarre sans fichier `.env`
- Tests : `APP_ENV` hérité du shell ou du conteneur de développement n'influence plus les tests (`tests/bootstrap.php`)

**documentation**

- update [La barre de debug et le profiler](boost/debug-bar.md) : nouvelles sections « Le flamegraph intégré » et « Comparer deux requêtes » (avec captures d'écran), mini-flamegraph de la barre de debug, paramètres `format=flame`, `order`, `compare`, `min` et `depth` de l'API
- new [Déployer son application](deploy/index.md) : préparation du serveur, `make:deploy`, variables d'environnement, déploiement, import de la base, dossiers partagés, commandes du quotidien, image de production
- update [La commande bin/edu](installation/command-edu.md) : commande `make:deploy`
- update [L'application dans Docker](installation/docker.md) : lien vers l'image de production

## v2.8.0 - 20/10/2026

Expand Down
Loading
Loading