Skip to content

Guides ​

Créer une page ​

phpx make:page génère un écran natif dans lib/pages/ (namespace Engine\App\) et un contrôleur backend jumeau dans lib/backend/src/Controller/ (namespace Backend\), puis enregistre les deux routes automatiquement — la route de l'écran dans public/index.php, la route API dans lib/backend/src/Kernel.php.

bash
bin/phpx make:page Profil /profil

Sans route explicite, elle est dérivée du nom en kebab-case (make:page Profil → /profil). Le fichier généré, prêt à modifier :

php
<?php

namespace Engine\App;

use Engine\Native\Scaffold;
use Engine\Native\Center;
use Engine\Native\Widget;
use Engine\Native\Text;
use Engine\Native\Tokens;

final class ProfilPage
{
    public static function build(float $screenWidth, float $screenHeight): Widget
    {
        return new Scaffold(
            new Center(new Text('ProfilPage', Tokens::TEXT_TITLE, Tokens::ink()->toHex(), bold: true)),
            $screenWidth,
            $screenHeight,
        );
    }
}

Un écran est une classe finale avec une seule méthode statique : build(float $screenWidth, float $screenHeight): Widget. Pas d'héritage, pas de cycle de vie à implémenter — le contrat entier tient dans l'interface Widget (layout()/paint()), voir plus bas.

Créer une entité ​

phpx make:entity génère une entité (lib/backend/src/Entity/) et un repository jumeau (lib/backend/src/Repository/) qui crée sa propre table SQLite au premier appel (CREATE TABLE IF NOT EXISTS) et expose find()/save() — à toi d'ajouter tes propres colonnes aux deux méthodes au fur et à mesure que l'entité grandit au-delà d'un simple id.

bash
bin/phpx make:entity Produit

Le repository généré utilise Engine\Database\Database::connection() (package phpnitro/database, Doctrine DBAL) — SQLite local par défaut, ou MySQL/PostgreSQL si DATABASE_URL est défini dans .env, sans changement de code.

Composer un écran avec les widgets natifs ​

Tout ce qui s'affiche implémente Engine\Native\Widget — un nœud d'un arbre à deux passes, exactement le contrat de RenderObject en Flutter : layout(Constraints): Size propose une taille, paint(Canvas, x, y) empile des commandes de dessin absolues. Le package phpnitro/ui (Engine\Native\*) fournit une cinquantaine de nœuds prêts à composer :

  • Mise en page — Flex::row()/::column(), Stack, Wrap, Padding, Align, Center, SizedBox, Container.
  • Écran — Scaffold (appBar/bottomNav/fab/drawer pinnés, le corps défile en dessous), AppBar, BottomNavigation, Drawer, Fab.
  • Texte — Text, RichText (multi-styles, wrap réel), TextSpan.
  • Contrôles — Button, Checkbox, Toggle, SelectBox, TextField, DatePicker/TimePicker, ConfirmButton, AlertButton.
  • Listes & cartes — ListTile, Card, Table, LazyList (liste virtualisée avec fenêtre de préchargement), Reorderable, Dismissible (glisser pour supprimer, 100% côté client).
  • Média & animation — Image, ImageCircle, IconCircle, Icon/MaterialIcons, Lottie, VideoPlayer, Animated, Hero, CustomPaint.
  • Autres — MapView (osmdroid/OpenStreetMap ou Mapbox/Google si configurés), GestureDetector, Async/AsyncTask, ClientTabs, PageView, Spinner, CircularProgress/ProgressBar, Banner, Divider.

Le catalogue complet documente chacun avec un exemple réel et son constructeur.

Exemple réel — un compteur persistant :

php
<?php

namespace Engine\App;

use Engine\Native\EdgeInsets;
use Engine\Native\Button;
use Engine\Native\Scaffold;
use Engine\Native\Container;
use Engine\Native\Flex;
use Engine\Native\Widget;
use Engine\Native\Padding;
use Engine\Native\Text;
use Engine\Native\Tokens;
use Engine\Preferences\Preferences;

final class CompteurPage
{
    public static function build(float $screenWidth, float $screenHeight): Widget
    {
        $count = (int) Preferences::get('compteur', '0');

        $body = new Padding(
            EdgeInsets::all(Tokens::SPACE_XL),
            Flex::column([
                new Text((string) $count, Tokens::TEXT_DISPLAY, Tokens::ink()->toHex(), bold: true),
                new Padding(
                    EdgeInsets::only(top: Tokens::SPACE_LG),
                    new Button('Incrémenter', 'increment'),
                ),
            ]),
        );

        return new Scaffold($body, $screenWidth, $screenHeight);
    }
}

Actions et état ​

Un widget interactif (Button, Tappable, ListTile...) prend une chaîne $action — pas un callback. Au tap, le client Kotlin fait le hit-test lui-même, appelle onTap(action, ...), qui reconnaît un préfixe connu (navigate:écran, back, tab:écran, toggle:champ, device:...) ou, à défaut, renvoie l'action telle quelle en ?action=... à la prochaine requête. Ton écran lit $_GET['action'] avant de construire son arbre :

php
// Dans public/index.php, avant de dispatcher vers l'écran :
if ($action === 'increment') {
    Preferences::set('compteur', (string) ((int) Preferences::get('compteur', '0') + 1));
}

Rien ne persiste entre deux requêtes en dehors de $_SESSION, Engine\Preferences\Preferences (clé-valeur persistant, JSON-encodé, package phpnitro/preferences) ou une vraie table via Engine\Database\Database.

Design tokens ​

Engine\Native\Tokens centralise l'échelle de design plutôt que des nombres choisis au cas par cas : espacements (SPACE_XS à SPACE_XXL), rayons (RADIUS_SM à RADIUS_PILL), échelle typographique (TEXT_CAPTION à TEXT_DISPLAY) et rôles de couleur (Tokens::ink(), ::inkSecondary(), ::inkMuted(), ::surface(), ::surfaceMuted(), ::border(), chacune renvoyant un Engine\Color avec ->toHex()). Utilise ces constantes plutôt que des valeurs brutes : c'est ce qui garde une app cohérente écran après écran.

phpnitro.yml — paiements, cartes, Firebase ​

phpnitro.yml, à la racine du projet, déclare le nom/description/version de l'app ainsi que les fournisseurs de paiement/cartes/Firebase utilisés — juste leurs noms, pas où sont leurs clés. Chaque fournisseur a un nom de variable .env fixe, défini une seule fois par le framework (voir paymentGatewayEnvVars()/mapProviderEnvVars() dans bin/phpx) :

yaml
name: Mon application
payments: [stripe, fedapay, paypal, razorpay, paydunya]
maps: [mapbox]
firebase: true

Les vraies valeurs vont dans .env sous ces noms fixes (STRIPE_PUBLIC_KEY/STRIPE_SECRET_KEY, MAPBOX_ACCESS_TOKEN, FIREBASE_SERVICE_ACCOUNT_JSON/FIREBASE_PROJECT_ID/FIREBASE_WEB_API_KEY...), rien à redéclarer dans phpnitro.yml. Trois commandes lisent ce fichier et croisent avec .env pour dire ce qui est réellement configuré, sans jamais afficher les clés : phpx payments, phpx maps, phpx firebase. phpx icon régénère l'icône de lancement Android (mipmaps + icône adaptative) depuis la clé icon (PNG carré) et icon_background.

Firebase

FirebaseAuth (connexion des utilisateurs finaux, clé web API client-safe) peut tourner depuis le PHP embarqué sur l'appareil. FirebaseMessaging (envoi de push FCM) et Firestore utilisent un compte de service — un vrai secret serveur — et doivent tourner depuis ton propre backend hébergé, jamais depuis le PHP qui s'exécute sur le téléphone de chaque utilisateur.

Publier sur Android ​

Un projet scaffoldé ne contient pas le moteur natif comme code source : android/app/build.gradle.kts déclare implementation("com.github.phpnitro:android-engine:v1.0.0"), résolu via JitPack depuis le dépôt github.com/phpnitro/android-engine (miroir en lecture seule du dossier android/engine/ du monorepo du framework) — exactement comme un projet Flutter référence le SDK Flutter sans le copier.

bash
bin/phpx build:android release

Génère android/app/build/outputs/apk/release/app-release.apk, signé selon android/app/build.gradle.kts/keystore.properties (copie keystore.properties.example et renseigne ton propre keystore avant un vrai build de release). La checklist complète (fiche Play Store, politique de confidentialité, captures) reste à ta charge.

PhpNitro est un produit FINANFA TECH — Publié sous licence MIT.