Ce guide part du principe que vous n’avez jamais créé de projet PHPAML. Nous n’allons pas passer directement de l’installation à une capture d’écran terminée. Chaque commande, dossier, suppression et ligne de PHP aura une raison claire. À la fin, vous disposerez d’une petite application AML View complète : un compteur dont la valeur change immédiatement dans le navigateur lorsque vous appuyez sur plus ou moins, sans demander au serveur de recalculer toute la page.
Le compteur est volontairement simple. Il permet d’observer tout le cycle frontend de PHPAML sans cacher les idées importantes derrière une grande application : PHP produit le document initial, AML View décrit l’interface, AML Engine active les actions déclarées et l’état client met uniquement à jour les éléments concernés.
1. Savoir exactement ce que vous allez construire
La page finale contient un titre, une explication, la valeur courante, un bouton moins, un bouton plus et une remise à zéro. La valeur commence à zéro. Plus ajoute un. Moins retire un, mais devient désactivé à zéro afin d’interdire les valeurs négatives. Réinitialiser ramène la valeur à zéro et devient également indisponible lorsqu’il n’y a rien à réinitialiser.
Vous allez créer le projet, le lancer, lire sa structure, retirer le contenu propre à la démonstration, écrire une page minimale, appliquer des classes CSS, vérifier la réactivité et comprendre ce qui se passe après chaque clic. Aucune base de données, API ou ligne de JavaScript personnalisée n’est nécessaire.
2. Vérifier l’installation autonome de PHPAML
Avec l’installateur officiel PHPAML, vous n’avez normalement pas besoin d’installer PHP ni Composer séparément. AML embarque son propre runtime PHP compatible ainsi qu’une copie privée de Composer. Le framework exige PHP 8.2 ou une version plus récente, mais ce prérequis est déjà satisfait par le runtime livré avec l’outil.
Ouvrez un terminal et lancez ces deux commandes :
aml version
aml doctorLa première commande confirme que le programme `aml` est accessible et affiche sa version. La seconde contrôle le runtime PHP réellement utilisé par AML, les extensions disponibles et les composants nécessaires au fonctionnement du CLI. Ce guide a été vérifié avec AML 1.7.0-beta.22 ; une version compatible plus récente peut naturellement afficher un autre numéro.
Ne lancez pas `php -v` comme contrôle principal de ce tutoriel. Cette commande interroge le PHP global de la machine, lorsqu’il existe, alors qu’AML privilégie son runtime privé. Il est donc possible que `php` soit absent du terminal tandis que `aml create-view-app` et `aml serve` fonctionnent parfaitement.
Certains projets avancés peuvent demander une extension PHP qui n’est pas incluse dans le runtime embarqué. Dans ce cas seulement, AML recherche automatiquement un autre exécutable PHP compatible disponible sur la machine. Le diagnostic indique clairement l’extension manquante. Pour ce premier compteur AML View, aucune installation PHP supplémentaire n’est attendue.
Si `aml version` est introuvable, installez d’abord PHPAML depuis la page officielle de téléchargement, fermez puis rouvrez le terminal et recommencez. Si `aml doctor` signale une erreur, corrigez ce diagnostic avant de créer le projet : continuer avec un environnement incomplet rendrait les erreurs suivantes beaucoup plus difficiles à comprendre.
3. Choisir un espace de travail propre
Placez-vous dans un emplacement où un nouveau dossier peut être créé. Le Bureau est pratique pour apprendre. Vérifiez qu’un dossier nommé premier-compteur ne contient pas déjà un travail important.
cd ~/DesktopLa commande de création refuse les écrasements dangereux. Malgré cela, une bonne habitude consiste à savoir exactement où la commande va écrire. Dans un dépôt professionnel, utilisez plutôt votre dossier de développement habituel.
4. Créer l’application AML View
Utilisez la commande dédiée à View. N’utilisez pas l’ancien parcours aml install view, qui a été retiré.
aml create-view-app premier-compteur
cd premier-compteurAML crée le projet, installe les versions compatibles de View et Engine, prépare l’autoload et vérifie que l’application peut charger FileApplication, EngineRuntime et les composants de sécurité. Si la création s’arrête avec une erreur, ne continuez pas avec un projet incomplet : lisez la dépendance signalée, corrigez-la puis relancez la commande dans un nouveau dossier vide.
5. Démarrer le serveur de développement
Lancez le projet depuis sa racine.
aml serveAML essaie d’abord http://127.0.0.1:8910. Si ce port est occupé, il tente 8911 puis le prochain port disponible. Utilisez exactement l’adresse affichée par le terminal. Gardez ce terminal ouvert : arrêter le processus arrête le serveur. Ouvrez un deuxième terminal pour les prochaines commandes.
Visitez l’adresse dans le navigateur. La démonstration générée doit apparaître sans erreur fatale. Cette première page réussie prouve que PHP, le runtime du projet et le point d’entrée public fonctionnent ensemble.
6. Comprendre la structure avant de la nettoyer
Les dossiers importants sont séparés volontairement.
premier-compteur/
public/
index.php
src/
controllers/
models/
views/
pages/
home/
page.php
components/
layouts/
states/
stylesheets/
themes/
routes/
runtime/
phpaml.jsonpublic/index.php est l’unique point d’entrée web et doit rester petit. src/views/pages contient les pages basées sur les fichiers. home/page.php correspond à /. components et layouts accueillent les structures visuelles réutilisables. stylesheets et themes appartiennent à l’interface. controllers et models restent disponibles pour le code serveur, même si notre compteur n’en a pas encore besoin. runtime contient le framework, les modules installés et les états générés : ne modifiez pas ses fichiers internes pour personnaliser l’application. phpaml.json contient la configuration du projet.
7. Définir ce que signifie réellement « nettoyer »
Nettoyer ne signifie pas supprimer chaque dossier inconnu. La page d’accueil générée démontre l’état, les appels API, les collections, les thèmes et les composants. Nous devons seulement remplacer cette démonstration par notre exercice ciblé. Gardez public/index.php, phpaml.json, runtime, src/controllers, src/models et src/views. Gardez aussi le layout et la navigation si vous souhaitez conserver l’enveloppe standard.
Pour cet exercice, remplacez entièrement src/views/pages/home/page.php et les règles de démonstration dans src/views/stylesheets/pages/home.css. Ne conservez pas d’anciennes classes qui ne sont plus rendues. Le CSS inutilisé rend le débogage difficile, car personne ne sait ensuite si un sélecteur est nécessaire. Ne supprimez pas les styles globaux, les variables des thèmes ou les pages d’état : ils servent toujours à l’application et aux erreurs.
8. Première modification : ouvrir et remplacer la page d’accueil
Le premier fichier à modifier est **`src/views/pages/home/page.php`**. Il correspond à la route d’accueil **`/`**. Gardez le terminal où `aml serve` fonctionne ouvert. Dans un deuxième terminal, placez-vous à la racine du projet et ouvrez uniquement ce fichier :
cd ~/Desktop/first-counter
code src/views/pages/home/page.phpSi `code` n’est pas reconnu, ouvrez le dossier `first-counter` dans VS Code, développez successivement `src`, `views`, `pages`, `home`, puis cliquez sur `page.php`. Ne créez pas un autre fichier portant le même nom. Dans `page.php`, sélectionnez tout le contenu existant, supprimez-le et collez cette première version complète :
<?php
declare(strict_types=1);
namespace App\Views\Pages\Home;
use AML\View\Page;
use AML\View\PageMetadata;
use AML\View\View;
use function AML\View\{Heading, MainContent, Section, Text, VStack};
final class HomePage extends Page
{
public function metadata(): PageMetadata
{
return new PageMetadata(
'Mon premier compteur PHPAML',
'Un petit compteur réactif construit avec AML View.',
);
}
public function body(): View
{
return MainContent(
Section(
VStack(
Text('PHPAML · PAS À PAS')->class('counter-eyebrow'),
Heading('Mon premier compteur réactif'),
Text('La valeur se met à jour directement dans le navigateur.'),
)->gap(16),
)->class('counter-page', 'shell'),
);
}
}Enregistrez précisément `src/views/pages/home/page.php` avec `⌘S` sur macOS ou `Ctrl+S` sur Windows et Linux. Revenez à l’adresse indiquée par `aml serve`, par exemple `http://127.0.0.1:8910`, puis actualisez. Vous devez voir le nouveau titre et la phrase, sans nombre ni bouton. Ce test prouve que vous modifiez le bon fichier avant d’introduire l’état.
Le namespace `App\Views\Pages\Home` correspond au chemin du dossier. `metadata()` décrit le document et `body()` décrit l’interface. Le fichier ne contient aucune balise HTML brute.
9. Deuxième modification : ajouter et afficher l’état
Restez dans **`src/views/pages/home/page.php`**. Aucun nouveau fichier n’est nécessaire. Nous ajoutons trois éléments : l’import `StateRef`, l’import `State`, puis la propriété `$count` dans la classe. Dans `body()`, la variable locale `$count` relie enfin cette propriété au texte visible.
Pour ne pas vous demander où insérer chaque ligne, remplacez de nouveau tout le contenu de `src/views/pages/home/page.php` par cette deuxième version complète :
<?php
declare(strict_types=1);
namespace App\Views\Pages\Home;
use AML\Engine\StateRef;
use AML\View\Page;
use AML\View\PageMetadata;
use AML\View\State;
use AML\View\View;
use function AML\View\{Heading, MainContent, Section, Text, VStack};
final class HomePage extends Page
{
#[State]
public int $count = 0;
public function metadata(): PageMetadata
{
return new PageMetadata(
'Mon premier compteur PHPAML',
'Un petit compteur réactif construit avec AML View.',
);
}
public function body(): View
{
$count = StateRef::to('count', $this->count);
return MainContent(
Section(
VStack(
Text('PHPAML · PAS À PAS')->class('counter-eyebrow'),
Heading('Mon premier compteur réactif'),
Text('La valeur se met à jour directement dans le navigateur.'),
Text($count)->class('counter-value'),
)->gap(16),
)->class('counter-page', 'shell'),
);
}
}Enregistrez ce même fichier et actualisez le navigateur. Le nombre `0` doit apparaître sous la phrase. Aucun bouton n’existe encore, donc le nombre ne peut pas changer. `#[State]` déclare une donnée réactive typée ; `StateRef::to('count', $this->count)` crée sa liaison cliente ; `Text($count)` désigne l’endroit exact à mettre à jour.
Si le navigateur affiche toujours uniquement le titre, vérifiez que `Text($count)` se trouve dans `VStack`, juste après la phrase. N’utilisez pas `Text((string) $this->count)` : ce texte serait calculé une fois par PHP et ne resterait pas lié à l’état.
10. Troisième modification : ajouter le bouton plus
Le fichier à modifier reste **`src/views/pages/home/page.php`**. Cette étape ajoute `ClientAction` en haut du fichier, `Button` dans l’import des fonctions, puis le bouton `+` immédiatement après `Text($count)`.
Remplacez tout le fichier par cette troisième version complète :
<?php
declare(strict_types=1);
namespace App\Views\Pages\Home;
use AML\Engine\ClientAction;
use AML\Engine\StateRef;
use AML\View\Page;
use AML\View\PageMetadata;
use AML\View\State;
use AML\View\View;
use function AML\View\{Button, Heading, MainContent, Section, Text, VStack};
final class HomePage extends Page
{
#[State]
public int $count = 0;
public function metadata(): PageMetadata
{
return new PageMetadata(
'Mon premier compteur PHPAML',
'Un petit compteur réactif construit avec AML View.',
);
}
public function body(): View
{
$count = StateRef::to('count', $this->count);
return MainContent(
Section(
VStack(
Text('PHPAML · PAS À PAS')->class('counter-eyebrow'),
Heading('Mon premier compteur réactif'),
Text('La valeur se met à jour directement dans le navigateur.'),
Text($count)->class('counter-value'),
Button('+')
->onClick(ClientAction::increment('count'))
->attribute('aria-label', 'Augmenter le compteur')
->class('counter-button', 'counter-button-plus'),
)->gap(16),
)->class('counter-page', 'shell'),
);
}
}Enregistrez `page.php`, actualisez une fois et cliquez sur `+` plusieurs fois. Vous devez observer `0 → 1 → 2 → 3` immédiatement, sans clignotement ni rechargement complet. PHP a fourni le document initial ; `ClientAction::increment('count')` est ensuite exécutée par AML Engine dans le navigateur.
Si le bouton apparaît sans réagir, contrôlez les imports dans ce même fichier : `ClientAction` vient de `AML\Engine`, tandis que `Button` doit se trouver dans la ligne `use function AML\View\{...};`.
11. Quatrième modification : ajouter le bouton moins et protéger zéro
Nous travaillons toujours dans **`src/views/pages/home/page.php`**. Ajoutez `Element` à l’import des fonctions. Le bouton plus isolé est remplacé par un conteneur qui regroupe moins et plus. Le bouton moins utilise `decrement` et `disabledWhen`.
Remplacez tout `page.php` par cette quatrième version :
<?php
declare(strict_types=1);
namespace App\Views\Pages\Home;
use AML\Engine\ClientAction;
use AML\Engine\StateRef;
use AML\View\Page;
use AML\View\PageMetadata;
use AML\View\State;
use AML\View\View;
use function AML\View\{Button, Element, Heading, MainContent, Section, Text, VStack};
final class HomePage extends Page
{
#[State]
public int $count = 0;
public function metadata(): PageMetadata
{
return new PageMetadata(
'Mon premier compteur PHPAML',
'Un petit compteur réactif construit avec AML View.',
);
}
public function body(): View
{
$count = StateRef::to('count', $this->count);
return MainContent(
Section(
VStack(
Text('PHPAML · PAS À PAS')->class('counter-eyebrow'),
Heading('Mon premier compteur réactif'),
Text('La valeur se met à jour directement dans le navigateur.'),
Text($count)->class('counter-value'),
Element('div',
Button('−')
->onClick(ClientAction::decrement('count'))
->disabledWhen($count, 0)
->attribute('aria-label', 'Diminuer le compteur')
->class('counter-button', 'counter-button-minus'),
Button('+')
->onClick(ClientAction::increment('count'))
->attribute('aria-label', 'Augmenter le compteur')
->class('counter-button', 'counter-button-plus'),
)->class('counter-controls'),
)->gap(18)->class('counter-card'),
)->class('counter-page', 'shell'),
);
}
}Enregistrez et actualisez. À zéro, le bouton moins doit être désactivé. Cliquez deux fois sur plus : la valeur atteint 2 et moins devient actif. Cliquez deux fois sur moins : la valeur revient à 0 et le bouton se désactive de nouveau. Cette vérification doit être faite maintenant, avant d’ajouter Réinitialiser.
Le conteneur `Element('div', ...)` sert uniquement à grouper les commandes. `disabledWhen($count, 0)` suit le même état que le nombre affiché et empêche l’utilisateur de descendre sous zéro.
12. Cinquième modification : ajouter Réinitialiser
Effectuez la dernière modification PHP dans **`src/views/pages/home/page.php`**. Le bouton Réinitialiser est placé entre moins et plus. Il fixe directement l’état à zéro avec `ClientAction::set`.
Remplacez une dernière fois tout le contenu de `page.php` par cette version complète :
<?php
declare(strict_types=1);
namespace App\Views\Pages\Home;
use AML\Engine\ClientAction;
use AML\Engine\StateRef;
use AML\View\Page;
use AML\View\PageMetadata;
use AML\View\State;
use AML\View\View;
use function AML\View\{Button, Element, Heading, MainContent, Section, Text, VStack};
final class HomePage extends Page
{
#[State]
public int $count = 0;
public function metadata(): PageMetadata
{
return new PageMetadata(
'Mon premier compteur PHPAML',
'Un petit compteur réactif construit avec AML View.',
);
}
public function body(): View
{
$count = StateRef::to('count', $this->count);
return MainContent(
Section(
VStack(
Text('PHPAML · PAS À PAS')->class('counter-eyebrow'),
Heading('Mon premier compteur réactif'),
Text('La valeur se met à jour directement dans le navigateur.'),
Text($count)->class('counter-value'),
Element('div',
Button('−')
->onClick(ClientAction::decrement('count'))
->disabledWhen($count, 0)
->attribute('aria-label', 'Diminuer le compteur')
->class('counter-button', 'counter-button-minus'),
Button('Réinitialiser')
->onClick(ClientAction::set('count', 0))
->disabledWhen($count, 0)
->class('counter-reset'),
Button('+')
->onClick(ClientAction::increment('count'))
->attribute('aria-label', 'Augmenter le compteur')
->class('counter-button', 'counter-button-plus'),
)->class('counter-controls'),
)->gap(18)->class('counter-card'),
)->class('counter-page', 'shell'),
);
}
}Enregistrez puis actualisez. Cliquez cinq fois sur plus, une fois sur moins et une fois sur Réinitialiser. La séquence attendue est `0 → 5 → 4 → 0`. À zéro, moins et Réinitialiser doivent être désactivés. Chaque version montrée depuis l’étape 8 était un fichier complet, enregistrable et testable : le lecteur n’a donc jamais à deviner où placer un extrait.
13. Vérifier que vous avez modifié les bons fichiers
À ce stade, un seul fichier PHP a changé : `src/views/pages/home/page.php`. Vous n’avez pas modifié `public/index.php`, les fichiers de `runtime`, les contrôleurs ou les modèles. Cette frontière est importante : la page décrit la vue, le point d’entrée démarre l’application et le runtime appartient au framework.
Si le compteur fonctionne mais paraît encore peu élégant, c’est normal. Le comportement PHP est terminé. L’étape suivante modifie un deuxième fichier, uniquement pour la présentation : **`src/views/stylesheets/pages/home.css`**.
14. Comprendre le résultat avant de passer au CSS
La propriété marquée `#[State]` fournit la valeur initiale. `StateRef` relie cette valeur aux éléments visibles. Les trois `ClientAction` décrivent diminuer, remettre à zéro et augmenter. Aucun clic ordinaire ne demande au serveur de recalculer toute la page.
Vous pouvez maintenant fermer `page.php` dans l’éditeur si vous le souhaitez. Ne collez pas le CSS dans ce fichier PHP. Ouvrez le fichier de styles indiqué à l’étape suivante.
15. Remplacer la feuille de style de la page
Ouvrez src/views/stylesheets/pages/home.css, retirez les règles de l’ancienne démonstration puis ajoutez uniquement des sélecteurs de classes utilisés par la nouvelle page.
.counter-page {
min-height: calc(100vh - 8rem);
display: grid;
place-items: center;
padding-block: 4rem;
}
.counter-card {
width: min(100%, 38rem);
padding: clamp(2rem, 6vw, 4.5rem);
align-items: center;
text-align: center;
border: 1px solid var(--line);
background: var(--panel);
box-shadow: var(--shadow);
}
.counter-eyebrow {
color: var(--lime);
font-size: .75rem;
font-weight: 850;
letter-spacing: .14em;
}
.counter-value {
min-width: 4ch;
color: var(--violet);
font: 800 clamp(4rem, 18vw, 8rem)/1 ui-monospace, monospace;
}
.counter-controls {
display: flex;
align-items: center;
justify-content: center;
gap: .75rem;
flex-wrap: wrap;
}
.counter-button,
.counter-reset {
min-width: 3.25rem;
min-height: 3.25rem;
border: 1px solid var(--line);
color: var(--ink);
background: var(--soft-fill);
font: inherit;
font-weight: 800;
cursor: pointer;
}
.counter-button {
font-size: 1.5rem;
}
.counter-button-plus {
border-color: var(--lime);
color: #17110d;
background: var(--lime);
}
.counter-button:disabled,
.counter-reset:disabled {
opacity: .4;
cursor: not-allowed;
}Ces sélecteurs correspondent aux classes ajoutées dans page.php. La feuille ne redéfinit pas tous les boutons, titres ou div de l’application. Les thèmes clair et sombre existants continuent à fournir les couleurs grâce aux variables CSS. AML View découvre récursivement les feuilles de style : aucun link ne doit être ajouté dans public/index.php.
16. Vérifier toute la séquence d’interaction
Ne vous arrêtez pas après un clic réussi. Suivez une séquence précise.
- Actualisez la page et vérifiez que la valeur vaut 0.
- Vérifiez que moins et Réinitialiser sont désactivés.
- Cliquez une fois sur plus et vérifiez que la valeur devient 1.
- Vérifiez que moins et Réinitialiser deviennent actifs.
- Cliquez encore quatre fois sur plus et vérifiez que la valeur atteint 5.
- Cliquez deux fois sur moins et vérifiez que la valeur revient à 3.
- Cliquez sur Réinitialiser et vérifiez le retour à 0.
- Vérifiez que moins et Réinitialiser redeviennent désactivés.
- Utilisez Tab puis activez chaque bouton disponible avec Entrée ou Espace.
- Réduisez la largeur comme sur un mobile et vérifiez la lisibilité des commandes.
Observez aussi ce qui ne doit pas arriver : aucun rechargement complet, aucune valeur négative, aucun nombre dupliqué, aucune balise HTML affichée comme texte et aucune erreur dans la console.
17. Comprendre pourquoi l’état revient à zéro après actualisation
Cliquez sur plus puis rechargez le navigateur. La valeur revient à zéro. C’est normal : #[State] crée un état réactif d’interface, pas un stockage permanent. PHP rend de nouveau le premier document et la valeur initiale de la propriété est zéro.
Pour mémoriser le compteur dans ce navigateur, vous pourriez ajouter Persisted('local', 'premier-compteur.valeur') à côté de State. Si la valeur doit appartenir à un utilisateur et rester accessible sur un autre appareil, exposez une route backend puis utilisez PHPAML Data. Ne confondez pas le stockage local du navigateur avec une donnée fiable du serveur. Dans ce premier guide, conserver le retour à zéro rend la frontière visible.
18. Diagnostiquer les erreurs courantes
Si la page s’affiche mais que les boutons ne réagissent pas, vérifiez que onClick reçoit une ClientAction et que le runtime Engine est présent. Si une classe est introuvable, comparez le namespace avec src/views/pages/home. Si le nombre reste à zéro, vérifiez que Text reçoit StateRef et non une chaîne déjà calculée. Si le style manque, confirmez que home.css se trouve sous src/views/stylesheets et que chaque nom de classe correspond exactement.
Si aml serve indique que le port est occupé, utilisez le nouveau port imprimé au lieu de supposer 8910. Si le navigateur affiche une ancienne version, actualisez normalement et confirmez que vous modifiez bien le projet servi dans le terminal. Ne réparez jamais une application générée en copiant des classes du framework au hasard dans public : recréez le projet lorsque son runtime installé est incomplet.
19. Réaliser une extension contrôlée
Quand la séquence de base fonctionne, essayez de modifier le pas. ClientAction::increment accepte un second nombre.
ClientAction::increment('count', 5)
ClientAction::decrement('count', 5)Avec un pas de cinq, réexaminez la règle du zéro : retirer cinq à une valeur inférieure à cinq pourrait produire un nombre négatif. Ce détail montre qu’un petit changement visuel peut introduire une règle métier. Gardez le pas de un jusqu’à ce que la nouvelle limite soit conçue explicitement.
En résumé
Vous êtes parti d’un emplacement vide, avez vérifié les outils, créé une application AML View, lancé son serveur, compris ses dossiers et nettoyé uniquement les fichiers propres à la démonstration. Vous avez ensuite créé un état typé, l’avez affiché avec StateRef et modifié grâce aux instructions déclaratives ClientAction. Moins et Réinitialiser réagissent au même état et protègent la limite zéro. Le CSS reste dans src/views/stylesheets et cible des classes explicites.
Vous avez surtout observé le modèle d’exécution PHPAML. PHP et AML View produisent le premier document. AML Engine active des instructions sûres dans le navigateur. Un clic ordinaire modifie l’état local et le texte lié sans demander au serveur de rendre toute la page. L’exercice naturel suivant consiste à persister une donnée utile avec une API et PHPAML Data, tout en gardant cette séparation entre interface, logique applicative et stockage.