Un micro-framework PHP puissant mais facile à utiliser, conçu pour vous aider à créer des applications Web dynamiques et robustes - rapidement !
Condensé dans un seul fichier d'environ 65 Ko, F3 (comme nous l'appelons affectueusement) vous offre une base solide, une base de code mature et une approche pragmatique de l'écriture d'applications Web. Sous le capot se trouvent un kit d'outils de développement Web facile à utiliser, un moteur de routage et de cache d'URL hautes performances, une mise en évidence du code intégrée et une prise en charge des applications multilingues. Il est léger, facile à utiliser et rapide. Surtout, cela ne vous gêne pas.
Que vous soyez un programmeur PHP novice ou expert, F3 vous permettra d'être opérationnel en un rien de temps. Aucune procédure d'installation inutile et fastidieuse. Aucune configuration complexe requise. Pas de structures de répertoires alambiquées. Il n’y a pas de meilleur moment que maintenant pour commencer à développer des applications Web en toute simplicité !
F3 prend en charge les bases de données SQL et NoSQL prêtes à l'emploi : MySQL, SQLite, MSSQL/Sybase, PostgreSQL, DB2 et MongoDB. Il est également livré avec de puissants mappeurs objet-relationnels pour l'abstraction et la modélisation de données qui sont tout aussi légers que le framework. Aucune configuration nécessaire.
Ce n'est pas tout. F3 est fourni avec d'autres plug-ins optionnels qui étendent ses capacités : -
Contrairement à d’autres frameworks, F3 vise à être utilisable – ce qui n’est pas habituel.
La philosophie derrière le framework et son approche de l'architecture logicielle sont orientées vers le minimalisme dans les composants structurels, évitant la complexité des applications et trouvant un équilibre entre l'élégance du code, les performances des applications et la productivité des programmeurs.
F3 dispose d’une architecture stable de classe entreprise. Des performances imbattables, des fonctionnalités conviviales et un encombrement léger. Que demander de plus ? Pour obtenir ce package, téléchargez simplement ce package ou visitez le référentiel fatfree-core pour trouver la dernière version Edge.
Pour tous les utilisateurs de Composer :
composer create-project bcosca/fatfree
composer require bcosca/fatfree-core
Il est fortement recommandé aux utilisateurs expérimentés de développer de nouvelles applications avec la dernière version pour profiter d'une base de code mise à jour et d'améliorations continues.
Le guide de l'utilisateur le plus récent et la documentation détaillée de l'API avec de nombreux exemples de code et un guide graphique sont disponibles sur fatfreeframework.com/.
Bien entendu, cette référence en ligne pratique est alimentée par F3 ! Il présente la capacité et les performances du framework. Vérifiez-le maintenant. Si vous souhaitez le lire directement sur github, vous pouvez trouver le contenu du site Web sur github.com/F3Community/F3com-data
Un designer sait qu’il a atteint la perfection non pas lorsqu’il n’y a plus rien à ajouter, mais lorsqu’il n’y a plus rien à retirer. --Antoine de Saint-Exupéry
Fat-Free Framework facilite la création de sites Web entiers en un tournemain. Avec la même puissance et la même brièveté que les boîtes à outils et bibliothèques Javascript modernes, F3 vous aide à écrire des programmes PHP plus beaux et plus fiables. Un simple coup d'œil sur votre code source PHP et tout le monde comprendra facilement tout ce que vous pouvez accomplir en si peu de lignes de code et la puissance des résultats.
F3 est l’un des frameworks les mieux documentés. L’apprendre ne coûte presque rien. Pas d'ensemble strict de structures de répertoires difficiles à naviguer et d'étapes de programmation intrusives. Pas de chargement d'options de configuration juste pour afficher 'Hello, World'
dans votre navigateur. Fat-Free vous offre beaucoup de liberté – et de style – pour accomplir plus de travail facilement et en moins de temps.
L'approche déclarative de F3 en matière de programmation permet aux novices comme aux experts de comprendre facilement le code PHP. Si vous êtes familier avec le langage de programmation Ruby, vous remarquerez la ressemblance entre les micro-framework Fat-Free et Sinatra car ils utilisent tous deux un langage simple spécifique à un domaine pour les services Web ReSTful. Mais contrairement à Sinatra et à ses incarnations PHP (Fitzgerald, Limonade, Glue - pour n'en nommer que quelques-uns), Fat-Free va au-delà de la simple gestion des itinéraires et des requêtes. Les vues peuvent prendre n'importe quelle forme, comme du texte brut, HTML, XML ou un message électronique. Le framework est livré avec un moteur de modèles rapide et facile à utiliser. F3 fonctionne également de manière transparente avec d'autres moteurs de modèles, notamment Twig, Smarty et PHP lui-même. Les modèles communiquent avec les mappeurs de données de F3 et l'assistant SQL pour des interactions plus complexes avec divers moteurs de bases de données. D'autres plug-ins étendent encore plus les fonctionnalités de base. Il s'agit d'un framework de développement Web complet - avec beaucoup de puissance !
Décompressez le contenu du package de distribution n’importe où sur votre disque dur. Par défaut, le fichier framework et les plug-ins facultatifs se trouvent dans le chemin lib/
. Organisez vos structures de répertoires comme vous le souhaitez. Vous pouvez déplacer les dossiers par défaut vers un chemin non accessible sur le Web pour une meilleure sécurité. Supprimez les plug-ins dont vous n'avez pas besoin. Vous pourrez toujours les restaurer plus tard et F3 détectera automatiquement leur présence.
Important : Si votre application utilise APC, Memcached, WinCache, XCache ou un cache de système de fichiers, effacez d'abord toutes les entrées du cache avant d'écraser une ancienne version du framework par une nouvelle.
Assurez-vous que vous utilisez la bonne version de PHP. F3 ne prend pas en charge les versions antérieures à PHP 7.2. Vous obtiendrez des erreurs de syntaxe (faux positifs) partout car les nouvelles constructions de langage et les fermetures/fonctions anonymes ne sont pas prises en charge par les versions obsolètes de PHP. Pour le savoir, ouvrez votre console ( bash
shell sous GNU/Linux, ou cmd.exe
sous Windows) :-
/path/to/php -v
PHP vous indiquera quelle version particulière vous utilisez et vous devriez obtenir quelque chose qui ressemble à ceci : -
PHP 7.4.21 (cli) (built: Jul 27 2021 15:56:07) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Xdebug v2.9.8, Copyright (c) 2002-2020, by Derick Rethans
Mettez à niveau si nécessaire et revenez ici si vous êtes passé à PHP 7.4 ou à une version ultérieure. Fatfree a besoin d'au moins PHP 7.2 pour fonctionner. Si vous avez besoin d'un fournisseur de services d'hébergement, essayez l'un de ces services :
Il est temps de commencer à rédiger notre première candidature : -
$ f3 = require ( ' path/to/base.php ' );
$ f3 -> route ( ' GET / ' ,
function () {
echo ' Hello, world! ' ;
}
);
$ f3 -> run ();
Ajoutez base.php
sur la première ligne avec le chemin approprié. Enregistrez le fragment de code ci-dessus sous index.php
dans votre dossier racine Web. Nous avons rédigé notre première page Web.
Vous utilisez le compositeur ? Ensuite, lancez simplement composer require bcosca/fatfree
et utilisez ce qui suit :
require ' vendor/autoload.php ' ;
$ f3 = Base:: instance ();
$ f3 -> route ( ' GET / ' ,
function () {
echo ' Hello, world! ' ;
}
);
$ f3 -> run ();
La première commande indique à l'interpréteur PHP que vous souhaitez que les fonctions et fonctionnalités du framework soient disponibles pour votre application. La méthode $f3->route()
informe Fat-Free qu'une page Web est disponible à l'URL relative indiquée par la barre oblique ( /
). Toute personne visitant votre site situé à l' http://www.example.com/
verra le message 'Hello, world!'
message car l'URL /
est équivalente à la page racine. Pour créer une route qui part de la page racine, comme http://www.example.com/inside/
, vous pouvez définir une autre route avec une simple chaîne GET /inside
.
L'itinéraire décrit ci-dessus indique au framework de restituer la page uniquement lorsqu'il reçoit une requête URL à l'aide de la méthode HTTP GET
. Les sites Web plus complexes contenant des formulaires utilisent d'autres méthodes HTTP comme POST
, et vous pouvez également les implémenter dans le cadre d'une spécification $f3->route()
.
Si le framework détecte une requête entrante pour votre page Web située à l'URL racine /
, il acheminera automatiquement la requête vers la fonction de rappel, qui contient le code nécessaire pour traiter la requête et restituer les éléments HTML appropriés. Dans cet exemple, nous envoyons simplement la chaîne 'Hello, world!'
au navigateur Web de l'utilisateur.
Nous avons donc établi notre premier itinéraire. Mais cela ne fera pas grand-chose, sauf faire savoir à F3 qu'il existe un processus qui le gérera et qu'il y a du texte à afficher sur le navigateur Web de l'utilisateur. Si vous avez beaucoup plus de pages sur votre site, vous devez configurer des itinéraires différents pour chaque groupe. Pour l’instant, restons simples. Pour demander au framework de commencer à attendre les requêtes, nous émettons la commande $f3->run()
.
Vous ne parvenez pas à exécuter l'exemple ? Si vous rencontrez des difficultés pour exécuter ce programme simple sur votre serveur, vous devrez peut-être modifier légèrement les paramètres de votre serveur Web. Jetez un œil à l'exemple de configuration Apache dans la section suivante (avec les équivalents Nginx et Lighttpd).
Vous rencontrez toujours des problèmes ? Assurez-vous que $f3 = require('path/to/base.php');
l'affectation précède toute sortie de votre script. base.php
modifie les en-têtes HTTP, donc tout caractère affiché dans le navigateur avant cette affectation provoquera des erreurs.
Notre premier exemple n’était pas trop difficile à avaler, n’est-ce pas ? Si vous aimez un peu plus de saveur dans votre soupe sans gras, insérez un autre itinéraire avant la commande $f3->run()
: -
$ f3 -> route ( ' GET /about ' ,
function () {
echo ' Donations go to a local charity... us! ' ;
}
);
Vous ne voulez pas encombrer l'espace de noms global avec des noms de fonctions ? Fat-Free reconnaît différentes manières de mapper les gestionnaires de routes aux classes et méthodes POO : -
class WebPage {
function display () {
echo ' I cannot object to an object ' ;
}
}
$ f3 -> route ( ' GET /about ' , ' WebPage->display ' );
Les requêtes HTTP peuvent également être acheminées vers des méthodes de classe statique : -
$ f3 -> route ( ' GET /login ' , ' Auth::login ' );
Les arguments passés sont toujours fournis comme deuxième paramètre :
$ f3 -> route ( ' GET /hello/@name ' , ' User::greet ' );
class User {
public static function greet ( $ f3 , $ args ) { // $ args is type of Array
echo " Hello " . $ args [ ' name ' ];
}
}
Si l'argument de nom fourni était foo (/hello/foo), le résultat suivant serait affiché :
Hello foo
En guise de démonstration du puissant langage spécifique au domaine (DSL) de Fat-Free, vous pouvez spécifier une seule route pour gérer différentes possibilités : -
$ f3 -> route ( ' GET /brew/@count ' ,
function ( $ f3 ) {
echo $ f3 -> get ( ' PARAMS.count ' ). ' bottles of beer on the wall. ' ;
}
);
Cet exemple montre comment nous pouvons spécifier un jeton @count
pour représenter une partie d'une URL. Le framework servira toute URL de requête correspondant au préfixe /brew/
, comme /brew/99
, /brew/98
, etc. Cela affichera '99 bottles of beer on the wall'
et '98 bottles of beer on the wall'
, respectivement. Fat-Free acceptera également une demande de page pour /brew/unbreakable
. (Attendez-vous à ce que cela affiche 'unbreakable bottles of beer on the wall'
.) Lorsqu'un tel itinéraire dynamique est spécifié, Fat-Free remplit automatiquement la variable globale du tableau PARAMS
avec la valeur des chaînes capturées dans l'URL. L'appel $f3->get()
à l'intérieur de la fonction de rappel récupère la valeur d'une variable framework. Vous pouvez certainement appliquer cette méthode dans votre code dans le cadre de la présentation ou de la logique métier. Mais nous en discuterons plus en détail plus tard.
Notez que Fat-Free comprend la notation par points du tableau. Vous pouvez plutôt utiliser la notation régulière PARAMS['count']
dans le code, qui est sujet aux erreurs de frappe et aux accolades déséquilibrées. Dans les vues et les modèles, le framework autorise la notation @PARAMS.count
qui est quelque peu similaire à Javascript. (Nous aborderons les vues et les modèles plus tard.)
Voici une autre façon d'accéder aux jetons dans un modèle de requête : -
$ f3 -> route ( ' GET /brew/@count ' ,
function ( $ f3 , $ params ) {
echo $ params [ ' count ' ]. ' bottles of beer on the wall. ' ;
}
);
Vous pouvez utiliser l'astérisque ( *
) pour accepter n'importe quelle URL après la route /brew
- si vous ne vous souciez pas vraiment du reste du chemin : -
$ f3 -> route ( ' GET /brew/* ' ,
function () {
echo ' Enough beer! We always end up here. ' ;
}
);
Un point important à considérer : vous serez confus (et vous-même) si vous avez à la fois GET /brew/@count
et GET /brew/*
ensemble dans la même application. Utilisez l'un ou l'autre. Autre chose : Fat-Free considère GET /brew
comme séparé et distinct de la route GET /brew/@count
. Chacun peut avoir des gestionnaires de route différents.
Attendez une seconde – dans tous les exemples précédents, nous n’avons jamais vraiment créé de répertoire sur notre disque dur pour stocker ces routes. La réponse courte : nous n’y sommes pas obligés. Tous les itinéraires F3 sont virtuels. Ils ne reflètent pas la structure des dossiers de notre disque dur. Si vous disposez de programmes ou de fichiers statiques (images, CSS, etc.) qui n'utilisent pas le framework - tant que les chemins d'accès à ces fichiers n'entrent pas en conflit avec un itinéraire défini dans votre application - votre logiciel serveur Web les livrera au navigateur de l'utilisateur, à condition que le serveur soit correctement configuré.
Lorsque vous définissez un itinéraire, vous pouvez lui attribuer un nom. Utilisez le nom de la route dans votre code et vos modèles au lieu d'une URL saisie. Ensuite, si vous devez modifier vos URL pour plaire aux seigneurs du marketing, il vous suffit d'effectuer la modification à l'endroit où l'itinéraire a été défini. Les noms de routes doivent suivre les règles de dénomination des variables PHP (pas de points, tirets ni tirets).
Nommons un itinéraire : -
$ f3 -> route ( ' GET @beer_list: /beer ' , ' Beer->list ' );
Le nom est inséré après le VERBE de route ( GET
dans cet exemple) précédé d'un symbole @
, et séparé de la partie URL par un symbole deux-points :
. Vous pouvez insérer un espace après les deux points si cela facilite la lecture de votre code (comme indiqué ici).
Pour accéder à la route nommée dans un modèle, récupérez la valeur de la route nommée comme clé du tableau ruche ALIASES
: -
< a href =" {{ @ALIASES.beer_list }} " > View beer list </ a >
Pour rediriger le visiteur vers une nouvelle URL, appelez la route nommée dans la méthode reroute()
comme : -
// a named route is a string value
$ f3 -> reroute ( ' @beer_list ' ); // note the single quotes
Si vous utilisez des jetons sur votre itinéraire, F3 remplacera ces jetons par leur valeur actuelle. Si vous souhaitez modifier la valeur du jeton avant d'appeler reroute, transmettez-le comme deuxième argument. :-
$ f3 -> route ( ' GET @beer_list: /beer/@country ' , ' Beer->bycountry ' );
$ f3 -> route ( ' GET @beer_list: /beer/@country/@village ' , ' Beer->byvillage ' );
// a set of key - value pairs is passed as argument to named route
$ f3 -> reroute ( ' @beer_list(@country=Germany) ' );
// if more than one token needed
$ f3 -> reroute ( ' @beer_list(@country=Germany,@village=Rhine) ' );
N'oubliez pas d' urlencode()
vos arguments si vous avez des caractères qui ne sont pas conformes aux directives RFC 1738 pour les URL bien formées.
La dernière version stable de PHP possède son propre serveur Web intégré. Démarrez-le en utilisant la configuration suivante : -
php -S localhost:80 -t /var/www/
La commande ci-dessus commencera à acheminer toutes les requêtes vers la racine Web /var/www
. Si une requête HTTP entrante pour un fichier ou un dossier est reçue, PHP la recherchera à la racine Web et l'enverra au navigateur si elle est trouvée. Sinon, PHP chargera le index.php
par défaut (contenant votre code compatible F3).
Si vous utilisez Apache, assurez-vous d'activer le module de réécriture d'URL (mod_rewrite) dans votre fichier apache.conf (ou httpd.conf). Vous devez également créer un fichier .htaccess contenant les éléments suivants : -
# Enable rewrite engine and route requests to framework
RewriteEngine On
# Some servers require you to specify the `RewriteBase` directive
# In such cases, it should be the path (relative to the document root)
# containing this .htaccess file
#
# RewriteBase /
RewriteRule ^(tmp)/|.ini$ - [R=404]
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
Le script indique à Apache que chaque fois qu'une requête HTTP arrive et si aucun fichier physique ( !-f
) ou chemin ( !-d
) ou lien symbolique ( !-l
) ne peut être trouvé, il doit transférer le contrôle à index.php
, qui contient notre contrôleur principal/frontal, et qui à son tour invoque le framework.
Le .htaccess file
contenant les directives Apache indiquées ci-dessus doit toujours être dans le même dossier que index.php
.
Vous devez également configurer Apache pour qu'il connaisse l'emplacement physique de index.php
sur votre disque dur. Une configuration typique est : -
DocumentRoot " /var/www/html "
< Directory "/var/www/html">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
Si vous développez plusieurs applications simultanément, une configuration d'hôte virtuel est plus facile à gérer : -
NameVirtualHost *
< VirtualHost *>
ServerName site1.com
DocumentRoot " /var/www/site1 "
< Directory "/var/www/site1">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
</ VirtualHost >
< VirtualHost *>
ServerName site2.com
DocumentRoot " /var/www/site2 "
< Directory "/var/www/site2">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
</ VirtualHost >
Chaque ServerName
( site1.com
et site2.com
dans notre exemple) doit être répertorié dans votre fichier /etc/hosts
. Sous Windows, vous devez modifier C:/WINDOWS/system32/drivers/etc/hosts
. Un redémarrage peut être nécessaire pour effectuer les modifications. Vous pouvez ensuite pointer votre navigateur Web vers l'adresse http://site1.com
ou http://site2.com
. Les hôtes virtuels facilitent grandement le déploiement de vos applications.
Pour les serveurs Nginx, voici la configuration recommandée (remplacez ip_address:port par les paramètres PHP FastCGI de votre environnement) : -
server {
root /var/www/html;
location / {
index index.php index.html index.htm;
try_files $uri $uri / /index.php? $query_string ;
}
location ~ .php$ {
fastcgi_pass ip_address:port;
include fastcgi_params;
}
}
Les serveurs Lighttpd sont configurés de la même manière : -
$HTTP["host"] =~ "www.example.com$" {
url.rewrite-once = ( "^/(.*?)(?.+)?$"=>"/index.php/$1?$2" )
server.error-handler-404 = "/index.php"
}
Installez le module de réécriture d'URL et le framework .NET approprié correspondant à votre version de Windows. Créez ensuite un fichier nommé web.config
à la racine de votre application avec le contenu suivant :
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Application" stopProcessing="true">
<match url=".*" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Revenons donc au codage. Vous pouvez déclarer une page obsolète et rediriger vos visiteurs vers un autre site/page : -
$ f3 -> route ( ' GET|HEAD /obsoletepage ' ,
function ( $ f3 ) {
$ f3 -> reroute ( ' /newpage ' );
}
);
Si quelqu'un tente d'accéder à l'URL http://www.example.com/obsoletepage
à l'aide d'une requête HTTP GET ou HEAD, le framework redirige l'utilisateur vers l'URL : http://www.example.com/newpage
comme indiqué dans le exemple ci-dessus. Vous pouvez également rediriger l'utilisateur vers un autre site, comme $f3->reroute('http://www.anotherexample.org/');
.
Le reroutage peut être particulièrement utile lorsque vous devez effectuer des travaux de maintenance sur votre site. Vous pouvez disposer d'un gestionnaire d'itinéraire qui informe vos visiteurs que votre site est hors ligne pendant une courte période.
Les redirections HTTP sont indispensables mais elles peuvent aussi être coûteuses. Dans la mesure du possible, évitez d'utiliser $f3->reroute()
pour envoyer un utilisateur vers une autre page du même site Web si vous pouvez diriger le flux de votre application en appelant la fonction ou la méthode qui gère la route cible. Toutefois, cette approche ne modifiera pas l'URL dans la barre d'adresse du navigateur Web de l'utilisateur. Si ce n'est pas le comportement que vous souhaitez et que vous avez vraiment besoin d'envoyer un utilisateur vers une autre page, dans des cas tels que la soumission réussie d'un formulaire ou après qu'un utilisateur a été authentifié, Fat-Free envoie un en-tête HTTP 302 Found
. Pour toutes les autres tentatives de redirection vers une autre page ou un autre site, le framework envoie un en-tête HTTP 301 Moved Permanently
.
Au moment de l'exécution, Fat-Free génère automatiquement une erreur HTTP 404 chaque fois qu'il constate qu'une requête HTTP entrante ne correspond à aucune des routes définies dans votre application. Cependant, il existe des cas où vous devez le déclencher vous-même.
Prenons par exemple une route définie comme GET /dogs/@breed
. La logique de votre application peut impliquer de rechercher dans une base de données et de tenter de récupérer l'enregistrement correspondant à la valeur de @breed
dans la requête HTTP entrante. Étant donné que Fat-Free acceptera n'importe quelle valeur après le préfixe /dogs/
en raison de la présence du jeton @breed
, l'affichage d'un message HTTP 404 Not Found
par programme devient nécessaire lorsque le programme ne trouve aucune correspondance dans notre base de données. Pour ce faire, utilisez la commande suivante : -
$ f3 -> error ( 404 );
L'architecture de Fat-Free est basée sur le concept selon lequel les URI HTTP représentent des ressources Web abstraites (non limitées au HTML) et chaque ressource peut passer d'un état d'application à un autre. Pour cette raison, F3 n'a aucune restriction sur la façon dont vous structurez votre application. Si vous préférez utiliser le modèle Modèle-Vue-Contrôleur, F3 peut vous aider à compartimenter les composants de votre application pour coller à ce paradigme. D'un autre côté, le cadre prend également en charge le modèle Ressource-Méthode-Représentation, et sa mise en œuvre est plus simple.
Voici un exemple d'interface ReST : -
class Item {
function get () {}
function post () {}
function put () {}
function delete () {}
}
$ f3 = require ( ' lib/base.php ' );
$ f3 -> map ( ' /cart/@item ' , ' Item ' );
$ f3 -> run ();
La méthode $f3->map()
de Fat-Free fournit une interface ReST en mappant les méthodes HTTP des routes vers les méthodes équivalentes d'un objet ou d'une classe PHP. Si votre application reçoit une requête HTTP entrante telle que GET /cart/123
, Fat-Free transférera automatiquement le contrôle à la méthode get()
de l'objet ou de la classe. En revanche, une requête POST /cart/123
sera acheminée vers la méthode post()
de la classe Item
.
Remarque : Les navigateurs n'implémentent pas les méthodes HTTP PUT
et DELETE
dans les formulaires HTML classiques. Ces méthodes et d'autres ReST ( HEAD
et CONNECT
) ne sont accessibles que via des appels AJAX au serveur.
Si le framework reçoit une requête HTTP pour une route qui correspond à une méthode qui n'est pas implémentée par une classe (peut-être avez-vous fait une erreur dans le mappage de route ou la méthode n'est pas encore écrite), il génère une HTTP 405 Method Not Allowed
Erreur HTTP 405 Method Not Allowed
.
Si un client demande OPTIONS
HTTP pour une ressource URL, F3 répond avec les en-têtes HTTP appropriés qui indiquent quelles méthodes sont autorisées pour la ressource (HEAD, GET, PUT, etc.). Le framework ne mappera pas la requête OPTIONS
à une classe.
Fat-Free permet de charger les classes uniquement au moment où vous en avez besoin, afin qu'elles ne consomment pas plus de mémoire qu'un segment particulier de votre application n'en a besoin. Et vous n'avez pas besoin d'écrire une longue liste d'instructions include
ou require
juste pour charger des classes PHP enregistrées dans différents fichiers et à différents emplacements. Le framework peut le faire automatiquement pour vous. Enregistrez simplement vos fichiers (une classe par fichier) dans un dossier et dites au framework de charger automatiquement le fichier approprié une fois que vous invoquez une méthode dans la classe : -
$ f3 -> set ( ' AUTOLOAD ' , ' autoload/ ' );
Vous pouvez attribuer un emplacement différent pour vos classes chargées automatiquement en modifiant la valeur de la variable globale AUTOLOAD
. Vous pouvez également avoir plusieurs chemins de chargement automatique. Si vos classes sont organisées et dans différents dossiers, vous pouvez demander au framework de charger automatiquement la classe appropriée lorsqu'une méthode statique est appelée ou lorsqu'un objet est instancié. Modifiez la variable AUTOLOAD
de cette façon : -
$ f3 -> set ( ' AUTOLOAD ' , ' admin/autoload/; user/autoload/; default/ ' );
Important : à l'exception de l'extension .php, le nom de la classe et le nom du fichier doivent être identiques pour que le framework charge automatiquement votre classe. Le nom de base de ce fichier doit être identique à votre invocation de classe, par exemple F3 recherchera soit Foo/BarBaz.php
, soit foo/barbaz.php
lorsqu'il détectera une new FooBarBaz
dans votre application.
AUTOLOAD
permet aux hiérarchies de classes de résider dans des sous-dossiers portant des noms similaires, donc si vous souhaitez que le framework charge automatiquement une classe avec espace de noms invoquée de la manière suivante : -
$ f3 -> set ( ' AUTOLOAD ' , ' autoload/ ' );
$ obj = new Gadgets iPad ;
Vous pouvez créer une hiérarchie de dossiers qui suit la même structure. En supposant /var/www/html/
est votre racine Web, alors F3 recherchera la classe dans /var/www/html/autoload/gadgets/ipad.php
. Le fichier ipad.php
doit avoir le code minimum suivant : -
namespace Gadgets ;
class iPad {}
N'oubliez pas : tous les noms de répertoires dans Fat-Free doivent se terminer par une barre oblique. Vous pouvez attribuer un chemin de recherche pour le chargeur automatique comme suit : -
$ f3 -> set ( ' AUTOLOAD ' , ' main/;aux/ ' );
F3, étant un framework prenant en charge les espaces de noms, vous permet d'utiliser une méthode dans une classe avec espace de noms en tant que gestionnaire de route, et il existe plusieurs façons de le faire. Pour appeler une méthode statique : -
$ f3 -> set ( ' AUTOLOAD ' , ' classes/ ' );
$ f3 -> route ( ' GET|POST / ' , ' MainHome::show ' );
Le code ci-dessus invoquera la méthode statique show()
de la classe Home
dans l'espace de noms Main
. La classe Home
doit être enregistrée dans le dossier classes/main/home.php
pour qu'elle soit chargée automatiquement.
Si vous préférez travailler avec des objets : -
$ f3 -> route ( ' GET|POST / ' , ' MainHome->show ' );
instanciera la classe Home
au moment de l'exécution et appellera ensuite la méthode show()
.
F3 dispose de quelques écouteurs d'événements de routage qui pourraient vous aider à améliorer le flux et la structure des classes de contrôleurs. Supposons que vous ayez un itinéraire défini comme suit : -
$ f3 -> route ( ' GET / ' , ' Main->home ' );
Si l'application reçoit une requête HTTP correspondant à la route ci-dessus, F3 instancie Main
, mais avant d'exécuter la méthode home()
, le framework recherche une méthode dans cette classe nommée beforeRoute()
. S'il est trouvé, F3 exécute le code contenu dans le gestionnaire d'événements beforeRoute()
avant de transférer le contrôle à la méthode home()
. Une fois cela accompli, le framework recherche un gestionnaire d’événements afterRoute()
. Comme beforeRoute()
, la méthode est exécutée si elle est définie.
Voici un autre goodie F3 : -
$ f3 -> route ( ' GET /products/@action ' , ' Products->@action ' );
Si votre application reçoit une demande pour, par exemple, /products/itemize
, F3 extraira la chaîne 'itemize'
de l'URL et la transmettra au jeton @action
dans le gestionnaire de route. F3 recherchera ensuite une classe nommée Products
et exécutera la méthode itemize()
.
Les gestionnaires d'itinéraires dynamiques peuvent avoir différentes formes : -
// static method
$ f3 -> route ( ' GET /public/@genre ' , ' Main::@genre ' );
// object mode
$ f3 -> route ( ' GET /public/@controller/@action ' , ' @controller->@action ' );
F3 déclenche une erreur HTTP 404 Not Found
au moment de l'exécution s'il ne peut pas transférer le contrôle à la classe ou à la méthode associée à la route actuelle, c'est-à-dire une classe ou une méthode non définie.
Les modèles de routage peuvent contenir des modificateurs qui demandent au framework de baser sa décision de routage sur le type de requête HTTP : -
$ f3 -> route ( ' GET /example [ajax] ' , ' Page->getFragment ' );
$ f3 -> route ( ' GET /example [sync] ' , ' Page->getFull ' );
La première instruction acheminera la requête HTTP vers le rappel Page->getFragment()
uniquement si un en-tête X-Requested-With: XMLHttpRequest
(objet AJAX) est reçu par le serveur. Si une requête ordinaire (synchrone) est détectée, F3 passera simplement au modèle correspondant suivant et, dans ce cas, il exécute le rappel Page->getFull()
.
Si aucun modificateur n'est défini dans un modèle de routage, les types de requêtes AJAX et synchrones sont acheminés vers le gestionnaire spécifié.
Les modificateurs de modèle de route sont également reconnus par $f3->map()
.
Les variables définies dans Fat-Free sont globales, c'est-à-dire qu'elles sont accessibles par n'importe quel composant MVC. Les globales du framework ne sont pas identiques aux globales PHP. Une variable F3 nommée content
n'est pas identique au $content
de PHP. F3 est un langage spécifique à un domaine à part entière et gère sa propre table de symboles distincte pour les variables système et d'application. Le framework, comme tout programme orienté objet bien conçu, ne pollue pas l'espace de noms global PHP avec des constantes, des variables, des fonctions ou des classes qui pourraient entrer en conflit avec n'importe quelle application. Contrairement à d'autres frameworks, F3 n'utilise pas l'instruction define()
de PHP. Toutes les constantes du framework sont confinées aux classes.
Pour attribuer une valeur à une variable Fat-Free :
$ f3 -> set ( ' var ' ,value); // or
$ f3 -> var =value;
$ f3 -> set ( ' hello.world ' , ' good morning ' ); // translates to : 'hello' == array ( 'world' = > 'good morning' )
$ f3 ->{ ' hello.world ' }= ' good morning ' ; // same as prior statement
Remarque : les variables Fat-Free acceptent tous les types de données PHP, y compris les objets et les fonctions anonymes.
Pour définir plusieurs variables à la fois :
$ f3 -> mset (
[
' foo ' => ' bar ' ,
' baz ' => 123
]
);
Pour récupérer la valeur d'une variable framework nommée var
:-
echo $ f3 -> get ( ' var ' ); // or
echo $ f3 -> var ;
Pour supprimer une variable Fat-Free de la mémoire si vous n'en avez plus besoin (jetez-la afin qu'elle n'interfère pas avec vos autres fonctions/méthodes), utilisez la méthode : -
$ f3 -> clear ( ' var ' ); // or
unset( $ f3 -> var );
Pour savoir si une variable a été définie précédemment : -
$ f3 -> exists ( ' var ' ) //
isset ( $ f3 -> var )
F3 maintient sa propre table de symboles pour les variables de framework et d'application, qui sont indépendantes de celles de PHP. Certaines variables sont mappées aux globales PHP. SESSION
de Fat-Free est équivalente à $_SESSION
et REQUEST
correspond à $_REQUEST
. L'utilisation de variables de framework est recommandée, au lieu de celles de PHP, pour vous aider dans le transfert de données entre différentes fonctions, classes et méthodes. Ils présentent également d'autres avantages : -
SESSION
modifie également $_SESSION
sous-jacent de PHP. La modification de ce dernier modifie également la contrepartie du cadre. Fat-Free ne maintient pas seulement un stockage stupide pour les variables et leurs valeurs. Il peut également automatiser la gestion des sessions et d'autres choses. L'attribution ou la récupération d'une valeur via la variable SESSION
de F3 démarre automatiquement la session. Si vous utilisez $_SESSION
(ou des fonctions liées à la session) directement, au lieu de la variable framework SESSION
, votre application devient responsable de la gestion des sessions.
En règle générale, les variables de structure ne persistent pas entre les requêtes HTTP. Seuls SESSION
et COOKIE
(et leurs éléments) qui sont mappés aux variables globales $_SESSION
et $_COOKIE
de PHP sont exemptés de la nature apatride de HTTP.
Il existe plusieurs variables globales prédéfinies utilisées en interne par Fat-Free, et vous pouvez certainement les utiliser dans votre application. Assurez-vous de savoir ce que vous faites. La modification de certaines variables globales Fat-Free peut entraîner un comportement inattendu du framework.
Le framework comporte plusieurs variables pour vous aider à organiser vos fichiers et vos structures de répertoires. Nous avons vu comment automatiser le chargement des classes en utilisant AUTOLOAD
. Il existe une variable globale UI
, qui contient le chemin pointant vers l'emplacement de vos vues/modèles HTML. DEBUG
est une autre variable que vous utiliserez assez souvent lors du développement d'applications et qui est utilisée pour définir la verbosité des traces d'erreurs.
Reportez-vous à la référence rapide si vous avez besoin d'une liste complète des variables de structure intégrées.
Une variable-cadre peut contenir n'importe quel nombre de lettres, de chiffres et de traits de soulignement. Il doit commencer par un caractère alphabétique et ne doit comporter aucun espace. Les noms de variables sont sensibles à la casse.
F3 utilise des majuscules pour les variables globales prédéfinies internes. Rien ne vous empêche d'utiliser des noms de variables composés de majuscules dans votre propre programme, mais en règle générale, respectez les minuscules (ou camelCase) lorsque vous configurez vos propres variables afin d'éviter tout conflit possible avec les versions actuelles et futures du framework. .
Vous ne devez pas utiliser de mots réservés PHP comme if
, for
, class
, default
, etc. comme noms de variables de framework. Cela peut entraîner des résultats imprévisibles.
F3 fournit également un certain nombre d'outils pour vous aider avec les variables de framework.
$ f3 -> set ( ' a ' , ' fire ' );
$ f3 -> concat ( ' a ' , ' cracker ' );
echo $ f3 -> get ( ' a ' ); // returns the string 'firecracker'
$ f3 -> copy ( ' a ' , ' b ' );
echo $ f3 -> get ( ' b ' ); // returns the same string : 'firecracker'
F3 fournit également quelques méthodes primitives pour travailler avec des variables de tableau : -
$ f3 -> set ( ' colors ' ,[ ' red ' , ' blue ' , ' yellow ' ]);
$ f3 -> push ( ' colors ' , ' green ' ); // works like PHP ' s array_push ()
echo $ f3 -> pop ( ' colors ' ); // returns 'green'
$ f3 -> unshift ( ' colors ' , ' purple ' ); // similar to array_unshift ()
echo $ f3 -> shift ( ' colors ' ); // returns 'purple'
$ f3 -> set ( ' grays ' ,[ ' light ' , ' dark ' ]);
$ result = $ f3 -> merge ( ' colors ' , ' grays ' ); // merges the two arrays
Contrairement à d’autres frameworks dotés de structures de dossiers rigides, F3 vous offre une grande flexibilité. Vous pouvez avoir une structure de dossiers qui ressemble à ceci (les mots entre parenthèses en majuscules représentent les variables du cadre F3 qui doivent être peaufinées) : -
/ (your Web root, where index.php is located)
app/ (application files)
dict/ (LOCALES, optional)
controllers/
logs/ (LOGS, optional)
models/
views/ (UI)
css/
js/
lib/ (you can store base.php here)
tmp/ (TEMP, used by the framework)
cache/ (CACHE)
N'hésitez pas à organiser vos fichiers et répertoires comme vous le souhaitez. Définissez simplement les variables globales F3 appropriées. Si vous souhaitez un site vraiment sécurisé, Fat-Free vous permet même de stocker tous vos fichiers dans un répertoire non accessible par le Web. La seule exigence est que vous laissiez index.php
, .htaccess
et vos fichiers publics, comme CSS, JavaScript, images, etc. dans un chemin visible par votre navigateur.
Fat-Free génère ses propres pages d'erreur HTML, avec des traces de pile pour vous aider dans le débogage. Voici un exemple : -
Erreur interne du serveur
strpos() attend au moins 2 paramètres, 0 étant donné
• var/html/dev/main.php:96 strpos() • var/html/dev/index.php:16 Base->run()
Si vous pensez que c'est un peu trop simple ou si vous souhaitez faire autre chose lorsque l'erreur se produit, vous pouvez créer votre propre gestionnaire d'erreurs personnalisé : -
$ f3 -> set ( ' ONERROR ' ,
function ( $ f3 ) {
// custom error handler code goes here
// use this if you want to display errors in a
// format consistent with your site ' s theme
echo $ f3 -> get ( ' ERROR.status ' );
}
);
F3 conserve une variable globale contenant les détails de la dernière erreur survenue dans votre application. La variable ERROR
est un tableau structuré comme suit : -
ERROR.code - displays the error code (404, 500, etc.)
ERROR.status - header and page title
ERROR.text - error context
ERROR.trace - stack trace
Lors du développement de votre application, il est préférable de définir le niveau de débogage au maximum afin de pouvoir retracer toutes les erreurs jusqu'à leur cause première : -
$ f3 -> set ( ' DEBUG ' , 3 );
Insérez simplement la commande dans la séquence d'amorçage de votre application.
Une fois que votre application est prête à être publiée, supprimez simplement la déclaration de votre application ou remplacez-la par : -
$ f3 -> set ( ' DEBUG ' , 0 );
Cela supprimera la sortie de trace de pile dans toute page d'erreur HTML générée par le système (car elle n'est pas destinée à être vue par les visiteurs de votre site).
DEBUG
peut avoir des valeurs allant de 0 (trace de pile supprimée) à 3 (le plus détaillé).
N'oubliez pas ! Les traces de pile peuvent contenir des chemins, des noms de fichiers, des commandes de base de données, des noms d'utilisateur et des mots de passe. Vous pouvez exposer votre site Web à des risques de sécurité inutiles si vous ne parvenez pas à définir la variable globale DEBUG
sur 0 dans un environnement de production.
Si votre application doit être configurable par l'utilisateur, F3 fournit une méthode pratique pour lire les fichiers de configuration afin de configurer votre application. De cette façon, vous et vos utilisateurs pouvez modifier l'application sans modifier le code PHP.
Au lieu de créer un script PHP contenant l'exemple de code suivant : -
$ f3 -> set ( ' num ' , 123 );
$ f3 -> set ( ' str ' , ' abc ' );
$ f3 -> set ( ' hash ' ,[ ' x ' => 1 , ' y ' => 2 , ' z ' => 3 ]);
$ f3 -> set ( ' items ' ,[ 7 , 8 , 9 ]);
$ f3 -> set ( ' mix ' ,[ ' this ' , 123.45 , FALSE ]);
Vous pouvez créer un fichier de configuration qui fait la même chose : -
[globals]
num =123
; this is a regular string
str =abc
; another way of assigning strings
str = " abc "
; this is an array
hash[x]=1
hash[y]=2
hash[z]=3
; dot-notation is recognized too
hash.x =1
hash.y =2
hash.z =3
; this is also an array
items =7,8,9
; array with mixed elements
mix = " this " ,123.45,FALSE
Au lieu de longues instructions $f3->set()
dans votre code, vous pouvez demander au framework de charger un fichier de configuration comme substitut de code. Gardons le texte ci-dessus sous setup.cfg. Nous pouvons alors l'appeler avec un simple : -
$ f3 -> config ( ' setup.cfg ' );
Les valeurs de chaîne n'ont pas besoin d'être citées, sauf si vous souhaitez inclure des espaces de début ou de fin. Si une virgule doit être traitée comme faisant partie d'une chaîne, placez-la entre guillemets doubles - sinon, la valeur sera traitée comme un tableau (la virgule est utilisée comme séparateur d'éléments de tableau). Les chaînes peuvent s'étendre sur plusieurs lignes : -
[globals]
str = " this is a
very long
string "
F3 vous donne également la possibilité de définir des routes HTTP dans les fichiers de configuration : -
[routes]
GET /=home
GET / 404 =App->page404
GET /page/@ num =Page->@controller
Les cartes de route peuvent également être définies dans les fichiers de configuration : -
[maps]
/ blog =BlogLogin
/blog/@ controller =Blog@controller
Les en-têtes de section [globals]
, [routes]
et [maps]
sont obligatoires. Vous pouvez combiner les deux sections dans un seul fichier de configuration - bien qu'il soit recommandé d'avoir [routes]
et [maps]
dans un fichier séparé. De cette façon, vous pouvez permettre aux utilisateurs finaux de modifier certains indicateurs spécifiques à l'application, tout en les empêchant de se mêler de votre logique de routage.
Une interface utilisateur telle qu'une page HTML doit être indépendante du code PHP sous-jacent lié au routage et à la logique métier. Ceci est fondamental pour le paradigme MVC. Une révision de base comme la conversion <h3>
en <p>
ne devrait pas nécessiter de modification du code de votre application. De la même manière, transformer une route simple comme GET /about
en GET /about-us
ne devrait avoir aucun effet sur votre interface utilisateur et votre logique métier (la vue et le modèle dans MVC, ou la représentation et la méthode dans RMR).
Mélanger les constructions de programmation et les composants de l'interface utilisateur dans un seul fichier, comme le codage de spaghetti, fait de la maintenance future des applications un cauchemar.
F3 prend en charge PHP comme moteur de modèle. Jetez un œil à ce fragment HTML enregistré comme template.htm
: -.
< p > Hello, < ?php echo $name; ? > ! </ p >
Si les balises courtes sont activées sur votre serveur, cela devrait également fonctionner: -
< p > Hello, < ?= $name ? > </ p >
Pour afficher ce modèle, vous pouvez avoir un code PHP qui ressemble à celui-ci (stocké dans un fichier distinct du modèle): -
$ f3 = require ( ' lib/base.php ' );
$ f3 -> route ( ' GET / ' ,
function ( $ f3 ) {
$ f3 -> set ( ' name ' , ' world ' );
$ view = new View ;
echo $ view -> render ( ' template.htm ' );
// Previous two lines can be shortened to : -
// echo View :: instance () - > render ( 'template.htm' );
}
);
$ f3 -> run ();
Le seul problème avec l'utilisation de PHP comme moteur de modèle, en raison du code PHP intégré dans ces fichiers, est l'effort conscient nécessaire pour respecter les directives sur la séparation des préoccupations et résister à la tentation de mélanger la logique métier avec votre interface utilisateur.
Comme alternative à PHP, vous pouvez utiliser le propre moteur de modèle de F3. Le fragment HTML ci-dessus peut être réécrit comme: -
< p > Hello, {{ @name }}! </ p >
et le code nécessaire pour afficher ce modèle: -
$ f3 = require ( ' lib/base.php ' );
$ f3 -> route ( ' GET / ' ,
function ( $ f3 ) {
$ f3 -> set ( ' name ' , ' world ' );
$ template = new Template ;
echo $ template -> render ( ' template.htm ' );
// Above lines can be written as : -
// echo Template :: instance () - > render ( 'template.htm' );
}
);
$ f3 -> run ();
Comme les jetons de routage utilisés pour attraper des variables dans les URL (rappelez-vous encore l'exemple GET /brew/@count
dans la section précédente?), Les jetons de modèle F3 commencent par le symbole @
suivi d'une série de lettres et de chiffres enfermés en accolades bouclées. Le premier caractère doit être alpha. Les jetons de modèle ont une correspondance individuelle avec les variables de cadre. Le cadre remplace automatiquement un jeton par la valeur stockée dans une variable du même nom.
Dans notre exemple, F3 remplace le jeton @name
dans notre modèle par la valeur que nous avons attribuée à la variable de nom. Au moment de l'exécution, la sortie du code ci-dessus sera: -
< p > Hello, world </ p >
Vous vous inquiétez des performances des modèles F3? Au moment de l'exécution, le framework analyse et compile / convertit un modèle F3 en code PHP la première fois qu'il est affiché via $template->render()
. Le cadre utilise ensuite ce code compilé dans tous les appels suivants. Par conséquent, les performances doivent être les mêmes que les modèles PHP, sinon mieux en raison de l'optimisation du code effectuée par le compilateur de modèle lorsque des modèles plus complexes sont impliqués.
Que vous utilisiez le modèle de modèle de PHP ou F3, le rendu du modèle peut être considérablement plus rapide si vous avez APC, WinCache ou XCache disponible sur votre serveur.
Comme mentionné précédemment, les variables de framework peuvent contenir n'importe quel type de données PHP. Cependant, l'utilisation de types de données non écaillés dans les modèles F3 peut produire des résultats étranges si vous ne faites pas attention. Les expressions en accolades bouclées seront toujours évaluées et converties en chaîne. Vous devez limiter vos variables d'interface utilisateur à des scalaires simples: - types de données string
, integer
, boolean
ou float
.
Mais qu'en est-il des tableaux? Sans gras reconnaît les tableaux et vous pouvez les utiliser dans vos modèles. Vous pouvez avoir quelque chose comme: -
< p > {{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }} </ p >
Et remplissez le tableau @buddy
dans votre code PHP avant de servir le modèle: -
$ f3 -> set ( ' buddy ' ,[ ' Tom ' , ' Dick ' , ' Harry ' ]);
Cependant, si vous insérez simplement {{ @buddy }}
dans votre modèle, PHP le remplacera par 'Array'
car il convertit le jeton en une chaîne. PHP, en revanche, générera un avis Array to string conversion
au moment de l'exécution.
F3 vous permet d'incorporer des expressions dans des modèles. Ces expressions peuvent prendre diverses formes, comme les calculs arithmétiques, les expressions booléennes, les constantes PHP, etc. Voici quelques exemples: -
{{ 2*(@page-1) }}
{{ (int)765.29+1.2e3 }}
< option value =" F " {{ @active? 'selected=" selected "':'' }} > Female </ option >
{{ var_dump(@xyz) }}
< p > That is {{ preg_match('/Yes/i',@response)?'correct':'wrong' }}! </ p >
{{ @obj- > property }}
Une note supplémentaire sur les expressions de table: prenez note que @foo.@bar
est une concaténation de chaîne $foo.$bar
), tandis que @foo.bar
se traduit par $foo['bar']
. Si $foo[$bar]
est ce que vous vouliez, utilisez la notation régulière @foo[@bar]
.
Les variables de cadre peuvent également contenir des fonctions anonymes:
$ f3 -> set ( ' func ' ,
function ( $ a , $ b ) {
return $ a . ' , ' . $ b ;
}
);
Le moteur de modèle F3 interprétera le jeton comme prévu, si vous spécifiez l'expression suivante:
{{ @func('hello','world') }}
La substitution variable simple est une chose que tous les moteurs de modèles ont. Sans gras a plus dans ses manches: -
< include href =" header.htm " />
La directive intégrera le contenu du modèle d'en-tête.htm à la position exacte où la directive est indiquée. Vous pouvez également avoir un contenu dynamique sous la forme de: -
< include href =" {{ @content }} " />
Une utilisation pratique pour une telle directive de modèle est lorsque vous avez plusieurs pages avec une disposition HTML commune mais avec un contenu différent. Instruire le framework pour insérer un sous-modèle dans votre modèle principal est aussi simple que d'écrire le code PHP suivant: -
// switch content to your blog sub - template
$ f3 -> set ( ' content ' , ' blog.htm ' );
// in another route , switch content to the wiki sub - template
$ f3 -> set ( ' content ' , ' wiki.htm ' );
Un sous-modèle peut à son tour contenir n'importe quel nombre de directives. F3 permet des modèles imbriqués illimités.
Vous pouvez spécifier des noms de fichiers avec autre chose que des extensions de fichiers .htm ou .html, mais il est plus facile de les prévisualiser dans votre navigateur Web pendant la phase de développement et de débogage. Le moteur de modèle ne se limite pas à rendre les fichiers HTML. En fait, vous pouvez utiliser le moteur de modèle pour rendre d'autres types de fichiers.
La directive <include>
dispose également d'un if
facultatif afin que vous puissiez spécifier une condition qui doit être satisfaite avant l'insertion du sous-modèle: -
< include if =" {{ count(@items) }} " href =" items.htm " />
Au cours de l'écriture / débogage de programmes F3 et de modèles de conception, il peut y avoir des cas où la désactivation de l'affichage d'un bloc de HTML peut être pratique. Vous pouvez utiliser la directive <exclude>
à cet effet: -
< exclude >
< p > A chunk of HTML we don't want displayed at the moment </ p >
</ exclude >
C'est comme la balise de commentaire <!-- comment -->
HTML, mais la directive <exclude>
rend le bloc HTML totalement invisible une fois le modèle rendu.
Voici une autre façon d'exclure le contenu du modèle ou d'ajouter des commentaires: -
{* < p > A chunk of HTML we don't want displayed at the moment </ p > *}
Une autre fonctionnalité de modèle utile est la directive <check>
. Il vous permet d'incorporer un fragment HTML en fonction de l'évaluation d'une certaine condition. Voici quelques exemples: -
< check if =" {{ @page=='Home' }} " >
< false > < span > Inserted if condition is false </ span > </ false >
</ check >
< check if =" {{ @gender=='M' }} " >
< true >
< div > Appears when condition is true </ div >
</ true >
< false >
< div > Appears when condition is false </ div >
</ false >
</ check >
Vous pouvez avoir autant de directives NESSIED <check>
que vous en avez besoin.
Une expression F3 à l'intérieur d'un attribut if qui équivaut à NULL
, une chaîne vide, un FALSE
booléen, un tableau vide ou un zéro, invoque automatiquement <false>
. Si votre modèle n'a pas de bloc <false>
, les balises d'ouverture et de fermeture <true>
sont facultatives: -
< check if =" {{ @loggedin }} " >
< p > HTML chunk to be included if condition is true </ p >
</ check >
Sans gras peut également gérer les blocs HTML répétitifs: -
< repeat group =" {{ @fruits }} " value =" {{ @fruit }} " >
< p > {{ trim(@fruit) }} </ p >
</ repeat >
L'attribut group
@fruits
à l'intérieur de la directive <repeat>
doit être un tableau et doit être défini dans votre code PHP en conséquence: -
$ f3 -> set ( ' fruits ' ,[ ' apple ' , ' orange ' , ' banana ' ]);
Rien n'est gagné en attribuant une valeur à @fruit
dans votre code d'application. Sans gras ignore toute valeur prédéfinie qu'elle peut avoir car elle utilise la variable pour représenter l'élément actuel pendant l'itération par rapport au groupe. La sortie du fragment de modèle HTML ci-dessus et le code PHP correspondant devient: -
< p > apple </ p >
< p > orange </ p >
< p > banana </ p >
Le framework permet une nidification illimitée des blocs <repeat>
: -
< repeat group =" {{ @div }} " key =" {{ @ikey }} " value =" {{ @idiv }} " >
< div >
< p > < span > < b > {{ @ikey }} </ b > </ span > </ p >
< p >
< repeat group =" {{ @idiv }} " value =" {{ @ispan }} " >
< span > {{ @ispan }} </ span >
</ repeat >
</ p >
</ div >
</ repeat >
Appliquez la commande F3 suivante: -
$ f3 -> set ( ' div ' ,
[
' coffee ' =>[ ' arabica ' , ' barako ' , ' liberica ' , ' kopiluwak ' ],
' tea ' =>[ ' darjeeling ' , ' pekoe ' , ' samovar ' ]
]
);
En conséquence, vous obtenez le fragment HTML suivant: -
< div >
< p > < span > < b > coffee </ b > </ span > </ p >
< p >
< span > arabica </ span >
< span > barako </ span >
< span > liberica </ span >
< span > kopiluwak </ span >
< p >
</ div >
< div >
< p > < span > < b > tea </ b > </ span > </ p >
< p >
< span > darjeeling </ span >
< span > pekoe </ span >
< span > samovar </ span >
</ p >
</ div >
Incroyable, n'est-ce pas? Et la seule chose que vous avez dû faire en PHP était de définir le contenu d'un seul div
variable F3 pour remplacer le jeton @div
. Sans gras facilite la programmation et la conception de modèles Web.
L'attribut value
de la directive <repeat>
de modèle renvoie la valeur de l'élément actuel dans l'itération. Si vous avez besoin d'obtenir la clé du tableau de l'élément actuel, utilisez plutôt l'attribut key
. L'attribut key
est facultatif.
<repeat>
a également un attribut de compteur facultatif qui peut être utilisé comme suit: -
< repeat group =" {{ @fruits }} " value =" {{ @fruit }} " counter =" {{ @ctr }} " >
< p class =" {{ @ctr%2?'odd':'even' }} " > {{ trim(@fruit) }} </ p >
</ repeat >
En interne, le moteur de modèle de F3 enregistre le nombre d'itérations de boucle et enregistre cette valeur dans la variable / token @ctr
, qui est utilisée dans notre exemple pour déterminer la classification impair / uniforme.
Si vous devez insérer des jetons F3 dans une section <script>
ou <style>
de votre modèle, le framework les remplacera toujours la manière habituelle: -
< script type =" text/javascript " >
function notify ( ) {
alert ( 'You are logged in as: {{ @userID }}' ) ;
}
</ script >
Les directives de modèle d'intégration à l'intérieur de vos balises <script>
ou <style>
ne nécessitent pas de manipulation spéciale: -
< script type =" text/javascript " >
var discounts = [ ] ;
< repeat group = "{{ @rates }}" value = "{{ @rate }}" >
// whatever you want to repeat in Javascript, e.g.
discounts.push(" { { @ rate } } ");
</ repeat >
</ script >
Par défaut, sans gras utilise le jeu de caractères UTF-8, sauf si modifié. Vous pouvez remplacer ce comportement en émettant quelque chose comme: -
$ f3 -> set ( ' ENCODING ' , ' ISO-8859-1 ' );
Une fois que vous avez informé le cadre du jeu de caractères souhaité, F3 l'utilisera dans tous les modèles HTML et XML jusqu'à sa modification.
Comme mentionné précédemment dans cette section, le cadre ne se limite pas aux modèles HTML. Vous pouvez également traiter les modèles XML. Les mécanismes sont à peu près similaires. Vous avez toujours les mêmes directives {{ @variable }}
et {{ expression }}
, <repeat>
, <check>
, <include>
et <exclude>
directives à votre disposition. Dites simplement à F3 que vous passez un fichier XML au lieu de HTML: -
echo Template:: instance ()-> render ( ' template.xml ' , ' application/xml ' );
Le deuxième argument représente le type de mime du document rendu.
Le composant View de MVC couvre tout ce qui ne relève pas du modèle et du contrôleur, ce qui signifie que votre présentation peut et doit inclure toutes sortes d'interfaces utilisateur, comme RSS, e-mail, RDF, FOAF, fichiers texte, etc. L'exemple Ci-dessous vous montre comment séparer votre présentation par e-mail de la logique métier de votre application: -
MIME-Version: 1.0
Content-type: text/html; charset={{ @ENCODING }}
From: {{ @from }}
To: {{ @to }}
Subject: {{ @subject }}
< p > Welcome, and thanks for joining {{ @site }}! </ p >
Enregistrez le modèle de messagerie ci-dessus comme Welcome.txt. Le code F3 associé serait: -
$ f3 -> set ( ' from ' , ' <[email protected]> ' );
$ f3 -> set ( ' to ' , ' <[email protected]> ' );
$ f3 -> set ( ' subject ' , ' Welcome ' );
ini_set ( ' sendmail_from ' , $ f3 -> get ( ' from ' ));
mail (
$ f3 -> get ( ' to ' ),
$ f3 -> get ( ' subject ' ),
Template:: instance ()-> render ( ' email.txt ' , ' text/html ' )
);
Astuce: remplacez la fonction SMTP Mail () par IMAP_MAIL () Si votre script communique avec un serveur IMAP.
Maintenant, n'est-ce pas quelque chose? Bien sûr, si vous avez un paquet de destinataires de courrier électronique, vous utiliseriez une base de données pour remplir les jetons FirstName, LastName et Email.
Voici une solution alternative utilisant le plug-in SMTP du F3: -
$ mail = new SMTP ( ' smtp.gmail.com ' , 465 , ' SSL ' , ' [email protected] ' , ' secret ' );
$ mail -> set ( ' from ' , ' <[email protected]> ' );
$ mail -> set ( ' to ' , ' "Slasher" <[email protected]> ' );
$ mail -> set ( ' subject ' , ' Welcome ' );
$ mail -> send (Template:: instance ()-> render ( ' email.txt ' ));
F3 prend en charge plusieurs langues dès la sortie de la boîte.
Tout d'abord, créez un fichier de dictionnaire avec la structure suivante (un fichier par langue): -
<?php
return [
' love ' => ' I love F3 ' ,
' today ' => ' Today is {0,date} ' ,
' pi ' => ' {0,number} ' ,
' money ' => ' Amount remaining: {0,number,currency} '
];
Enregistrez-le en tant que dict/en.php
. Créons un autre dictionnaire, cette fois pour l'allemand. Enregistrez le fichier en tant que dict/de.php
: -
<?php
return [
' love ' => ' Ich liebe F3 ' ,
' today ' => ' Heute ist {0,date} ' ,
' money ' => ' Restbetrag: {0,number,currency} '
];
Les dictionnaires ne sont rien de plus que des paires de valeurs clés. F3 instancie automatiquement les variables du cadre basées sur les clés des fichiers linguistiques. En tant que tel, il est facile d'incorporer ces variables sous forme de jetons dans vos modèles. Utilisation du moteur de modèle F3: -
< h1 > {{ @love }} </ h1 >
< p >
{{ @today,time() | format }}. < br />
{{ @money,365.25 | format }} < br />
{{ @pi }}
</ p >
Et la version plus longue qui utilise PHP comme moteur de modèle: -
<?php $ f3 =Base:: instance (); ?>
<h1> <?php echo $ f3 -> get ( ' love ' ); ?> </h1>
<p>
<?php echo $ f3 -> get ( ' today ' , time ()); ?> .<br />
<?php echo $ f3 -> get ( ' money ' , 365.25 ); ?>
<?php echo $ f3 -> get ( ' pi ' ); ?>
</p>
Ensuite, nous demandons à F3 de rechercher des dictionnaires dans le dict/
dossier: -
$ f3 -> set ( ' LOCALES ' , ' dict/ ' );
Mais comment le cadre détermine-t-il quelle langue utiliser? F3 le détectera automatiquement en examinant d'abord les en-têtes de demande HTTP, en particulier l'en-tête Accept-Language
envoyée par le navigateur.
Pour remplacer ce comportement, vous pouvez déclencher F3 pour utiliser une langue spécifiée par l'utilisateur ou l'application: -
$ f3 -> set ( ' LANGUAGE ' , ' de ' );
Remarque: Dans l'exemple ci-dessus, le PI clé n'existe que dans le dictionnaire anglais. Le cadre utilisera toujours l'anglais ( en
) comme un secours pour remplir des clés qui ne sont pas présentes dans la langue spécifiée (ou détectée).
Vous pouvez également créer des fichiers de dictionnaire pour des variantes de langue comme en-US
, es-AR
, etc. Dans ce cas, F3 utilisera d'abord la variante linguistique (comme es-AR
). S'il y a des clés qui n'existent pas dans la variante, le framework recherchera la clé dans les langues racine ( es
), puis utilise le fichier en
langue en comme se repliez final. Les paires de valeurs clés du dictionnaire deviennent des variables F3 une fois référencées. Assurez-vous que les clés ne sont en conflit avec aucune variable de framework instanciée via $f3->set()
, $f3->mset()
ou $f3->config()
.
Avez-vous remarqué le modèle particulier 'Today is {0,date}'
dans notre exemple précédent? La capacité multilingue de F3 dépend des règles de formatage des chaînes / messages du projet de soins intensifs. Le framework utilise son propre sous-ensemble de l'implémentation de formatage des chaînes de soins intensifs. Il n'est pas nécessaire que l'extension intl
de PHP soit activée sur le serveur.
Une autre chose: F3 peut également charger des fichiers formatés de style .ini comme dictionnaires: -
love = " I love F3 "
today = " Today is {0,date} "
pi = " {0,number} "
money = " Amount remaining: {0,number,currency} "
Enregistrez-le en tant que dict/en.ini
afin que le framework puisse le charger automatiquement.
Par défaut, le gestionnaire de vue et le moteur de modèle échappent à toutes les variables rendues, c'est-à-dire converties en entités HTML pour vous protéger des éventuelles attaques XSS et d'injection de code. D'un autre côté, si vous souhaitez passer des fragments HTML valides de votre code d'application à votre modèle: -
$ f3 -> set ( ' ESCAPE ' , FALSE );
Cela peut avoir des effets indésirables. Vous ne voudrez peut-être pas que toutes les variables passent par l'interception. Sans gras vous permet de vous désabuser individuellement. Pour les modèles F3: -
{{ @html_content | raw }}
Dans le cas des modèles PHP: -
<?php echo View:: instance ()-> raw ( $ html_content ); ?>
En ajout à l'absence automatique des variables F3, le framework vous donne également une main libre dans la saisie des utilisateurs de désinfection des formulaires HTML: -
$ f3 -> scrub ( $ _GET , ' p; br; span; div; a ' );
Cette commande supprimera toutes les balises (à l'exception de celles spécifiées dans le deuxième argument) et des caractères dangereux de la variable spécifiée. Si la variable contient un tableau, chaque élément du tableau est désinfecté récursivement. Si un astérisque (*) est passé comme le deuxième argument, $f3->scrub()
permet à toutes les balises HTML de passer par intacte et de supprimer simplement des caractères de contrôle dangereux.
Sans gras est conçu pour faire un travail d'interfaçage avec les bases de données SQL. Si vous n'êtes pas du genre à vous plonger dans les détails de SQL, mais que vous vous penchez davantage sur la gestion des données orientée objet, vous pouvez vous rendre directement à la section suivante de ce didacticiel. Cependant, si vous devez effectuer des tâches complexes de gestion des données et d'optimisation des performances de la base de données, SQL est la voie à suivre.
L'établissement de la communication avec un moteur SQL comme MySQL, SQLite, SQL Server, Sybase et Oracle se fait en utilisant la commande familière $f3->set()
. La connexion à une base de données SQLite serait: -
$ db = new DB SQL ( ' sqlite:/absolute/path/to/your/database.sqlite ' );
Un autre exemple, cette fois avec mysql: -
$ db = new DB SQL (
' mysql:host=localhost;port=3306;dbname=mysqldb ' ,
' admin ' ,
' p455w0rD '
);
D'ACCORD. C'était facile, n'est-ce pas? C'est à peu près comme ça que tu ferais la même chose dans PHP ordinaire. Vous avez juste besoin de connaître le format DSN de la base de données à laquelle vous vous connectez. Voir la section PDO du manuel PHP.
Continuons notre code PHP: -
$ f3 -> set ( ' result ' , $ db -> exec ( ' SELECT brandName FROM wherever ' ));
echo Template:: instance ()-> render ( ' abc.htm ' );
Huh, que se passe-t-il ici? Ne devrions-nous pas mettre en place des choses comme les PDO, les déclarations, les curseurs, etc.? La réponse simple est: vous n'avez pas à le faire. F3 simplifie tout en prenant soin de tout le travail acharné du backend.
Cette fois, nous créons un modèle HTML comme abc.htm
qui a au minimum ce qui suit: -
< repeat group =" {{ @result }} " value =" {{ @item }} " >
< span > {{ @item.brandName }} </ span >
</ repeat >
Dans la plupart des cas, le jeu de commandes SQL devrait être suffisant pour générer un résultat prêt pour le Web afin que vous puissiez utiliser directement la variable de tableau result
dans votre modèle. Quoi qu'il en soit, sans gras ne vous empêchera pas d'entrer dans ses internes de gestionnaire SQL. En fait, la classe DBSQL
de F3 découle directement de la classe PDO
de PHP, vous avez donc toujours accès aux composants et primitives PDO sous-jacents impliqués dans chaque processus, si vous avez besoin d'un contrôle de grains fins.
Voici un autre exemple. Au lieu d'une seule instruction fournie comme argument à la commande $db->exec()
, vous pouvez également transmettre un tableau d'instructions SQL: -
$ db -> exec (
[
' DELETE FROM diet WHERE food="cola" ' ,
' INSERT INTO diet (food) VALUES ("carrot") ' ,
' SELECT * FROM diet '
]
);
F3 est assez intelligent pour savoir que si vous passez un tableau d'instructions SQL, cela indique une transaction par lots SQL. Vous n'avez pas à vous soucier des rétroviseurs SQL et des engagements, car le cadre reviendra automatiquement à l'état initial de la base de données si une erreur se produit pendant la transaction. En cas de succès, F3 commet toutes les modifications apportées à la base de données.
Vous pouvez également démarrer et mettre fin à une transaction par programme: -
$ db -> begin ();
$ db -> exec ( ' DELETE FROM diet WHERE food="cola" ' );
$ db -> exec ( ' INSERT INTO diet (food) VALUES ("carrot") ' );
$ db -> exec ( ' SELECT * FROM diet ' );
$ db -> commit ();
Un retour en arrière se produira si l'une des instructions rencontre une erreur.
Pour obtenir une liste de toutes les instructions de la base de données émises: -
echo $ db -> log ();
Passer des arguments de chaîne aux instructions SQL est chargé de danger. Considérez ceci: -
$ db -> exec (
' SELECT * FROM users ' .
' WHERE username=" ' . $ f3 -> get ( ' POST.userID ' . ' " ' )
);
Si la variable POST
userID
ne passe par aucun processus d'assainissement des données, un utilisateur malveillant peut passer la chaîne suivante et endommager votre base de données de manière irréversible: -
admin " ; DELETE FROM users; SELECT " 1
Heureusement, les requêtes paramétrées vous aident à atténuer ces risques: -
$ db -> exec (
' SELECT * FROM users WHERE userID=? ' ,
$ f3 -> get ( ' POST.userID ' )
);
Si F3 détecte que la valeur du paramètre / jeton de requête est une chaîne, la couche d'accès aux données sous-jacente échappe à la chaîne et ajoute des devis si nécessaire.
Notre exemple dans la section précédente sera beaucoup plus sûr de l'injection SQL si elle est écrite de cette façon: -
$ db -> exec (
[
' DELETE FROM diet WHERE food=:name ' ,
' INSERT INTO diet (food) VALUES (?) ' ,
' SELECT * FROM diet '
],
[
array ( ' :name ' => ' cola ' ),
array ( 1 => ' carrot ' ),
NULL
]
);
F3 est rempli de mappeurs (ORMS) faciles à utiliser des objets (ORMS) qui se trouvent entre votre application et vos données - ce qui vous rend beaucoup plus facile et plus rapide pour vous rédiger des programmes qui gèrent les opérations de données courantes - comme la création, la récupération, la mise à jour, et supprimer les informations (CRUD) des bases de données SQL et NOSQL. Les mappeurs de données effectuent la plupart des travaux en mappant les interactions d'objets PHP aux requêtes backend correspondantes.
Supposons que vous ayez une base de données MySQL existante contenant un tableau d'utilisateurs de votre application. (SQLite, PostgreSQL, SQL Server, Sybase fera aussi bien.) Il aurait été créé en utilisant la commande SQL suivante: -
CREATE TABLE users (
userID VARCHAR ( 30 ),
password VARCHAR ( 30 ),
visits INT ,
PRIMARY KEY (userID)
);
Remarque: MongoDB est un moteur de base de données NoSQL et sans schéma. F3 a sa propre implémentation NOSQL rapide et légère appelée JIG, qui utilise des fichiers plats sérialisés par PHP ou codés par JSON. Ces couches d'abstraction ne nécessitent aucune structure de données rigides. Les champs peuvent varier d'un record à un autre. Ils peuvent également être définis ou abandonnés à la volée.
Revenons maintenant à SQL. Tout d'abord, nous établissons la communication avec notre base de données.
$ db = new DB SQL (
' mysql:host=localhost;port=3306;dbname=mysqldb ' ,
' admin ' ,
' wh4t3v3r '
);
Pour récupérer un enregistrement de notre table: -
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID=? ' , ' tarzan ' ]);
La première ligne instancie un objet de mappeur de données qui interagit avec la table users
dans notre base de données. Derrière la scène, F3 récupère la structure de la table users
et détermine quel (s) champ (s) sont définis comme les clés primaires. À ce stade, l'objet de mappeur ne contient pas encore de données (état sec), donc $user
n'est rien de plus qu'un objet structuré - mais il contient les méthodes dont il a besoin pour effectuer les opérations de CRUD de base et certains extras. Pour récupérer un enregistrement de la table des utilisateurs avec un champ userID
contenant la valeur de chaîne tarzan
, nous utilisons la load() method
. Ce processus est appelé "hydratation automatique" l'objet de mappeur de données.
Facile, n'est-ce pas? F3 comprend qu'un tableau SQL a déjà une définition structurelle existant dans le moteur de la base de données elle-même. Contrairement à d'autres cadres, F3 ne nécessite aucune déclaration de classe supplémentaire (à moins que vous ne souhaitiez étendre les mappeurs de données pour s'adapter aux objets complexes), pas de mappages de propriétés PHP / objet redondants (Duplication des efforts), pas de générateurs de code (qui nécessitent du code Régénération Si la structure de la base de données change), pas de fichiers XML / YAML stupides pour configurer vos modèles, pas de commandes superflues juste pour récupérer un seul enregistrement. Avec F3, un redimensionnement simple d'un champ varchar
dans MySQL ne demande pas de modification de votre code d'application. Conformément à MVC et à la "séparation des préoccupations", l'administrateur de la base de données a autant de contrôle sur les données (et les structures) qu'un concepteur de modèle a sur des modèles HTML / XML.
Si vous préférez travailler avec les bases de données NOSQL, les similitudes dans la syntaxe de requête sont superficielles. Dans le cas du mappeur de données MongoDB, le code équivalent serait: -
$ db = new DB Mongo ( ' mongodb://localhost:27017 ' , ' testdb ' );
$ user = new DB Mongo Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID ' => ' tarzan ' ]);
Avec le gabarit, la syntaxe est similaire au moteur de modèle de F3: -
$ db = new DB Jig ( ' db/data/ ' , DB Jig:: FORMAT_JSON );
$ user = new DB Jig Mapper ( $ db , ' users ' );
$ user -> load ([ ' @userID=? ' , ' tarzan ' ]);
Le framework mappe automatiquement les visits
sur le terrain dans notre tableau dans une propriété de mappeur de données pendant l'instanciation des objets, c'est-à-dire $user=new DBSQLMapper($db,'users');
. Une fois l'objet créé, $user->password
et $user->userID
mapperaient respectivement les champs password
et userID
de notre tableau.
Vous ne pouvez pas ajouter ou supprimer un champ mappé, ou modifier la structure d'une table à l'aide de l'ORM. Vous devez le faire dans MySQL, ou quel que soit le moteur de base de données que vous utilisez. Après avoir apporté les modifications à votre moteur de base de données, sans gras synchronisera automatiquement la nouvelle structure de table avec votre objet de mappeur de données lorsque vous exécutez votre application.
F3 tire la structure du mappeur de données directement du schéma de la base de données. Aucune conjecture impliquée. Il comprend les différences entre les moteurs de base de données MySQL, SQLite, MSSQL, Sybase et PostgreSQL.
Les identifiants SQL ne doivent pas utiliser les mots réservés et doivent être limités aux caractères alphanumériques AZ
, 0-9
et le symbole de trait de soulignement ( _
). Les noms de colonne contenant des espaces (ou des caractères spéciaux) et entourés de citations dans la définition de données ne sont pas compatibles avec l'ORM. Ils ne peuvent pas être représentés correctement en tant que propriétés d'objets PHP.
Disons que nous voulons incrémenter le nombre de visites de l'utilisateur et mettre à jour l'enregistrement correspondant dans le tableau de nos utilisateurs, nous pouvons ajouter le code suivant: -
$ user -> visits ++;
$ user -> save ();
Si nous voulions insérer un enregistrement, nous suivons ce processus: -
$ user = new DB SQL Mapper ( $ db , ' users ' );
// or $ user = new DB Mongo Mapper ($ db , 'users' );
// or $ user = new DB Jig Mapper ($ db , 'users' );
$ user -> userID = ' jane ' ;
$ user -> password = password_hash ( ' secret ' , PASSWORD_BCRYPT , [ ' cost ' => 12 ]);
$ user -> visits = 0 ;
$ user -> save ();
Nous utilisons toujours la même méthode save()
. Mais comment F3 sait-il quand un enregistrement doit être inséré ou mis à jour? Au moment où un objet de mappeur de données est auto-hydraté par une récupération d'enregistrement, le cadre maintient la trace des clés principales de l'enregistrement (ou _id
, dans le cas de MongoDB et Jig) - il sait donc quel enregistrement doit être mis à jour ou supprimé - même Lorsque les valeurs des clés primaires sont modifiées. Un mappeur de données hydraté par programme - dont les valeurs n'ont pas été récupérées de la base de données, mais peuplées par l'application - n'auront aucune mémoire des valeurs précédentes dans ses clés principales. Il en va de même pour MongoDB et Jig, mais en utilisant l'objet _id
comme référence. Ainsi, lorsque nous avons instancié l'objet $user
ci-dessus et rempli ses propriétés avec des valeurs de notre programme - sans récupérer un enregistrement de la table utilisateur, F3 sait qu'il devrait insérer cet enregistrement.
Un objet mappeur ne sera pas vide après un save()
. Si vous souhaitez ajouter un nouvel enregistrement à votre base de données, vous devez d'abord déshydrater le mappeur: -
$ user -> reset ();
$ user -> userID = ' cheetah ' ;
$ user -> password = password_hash ( ' unknown ' , PASSWORD_BCRYPT , [ ' cost ' => 12 ]);
$ user -> save ();
Appeler save()
Une deuxième fois sans inviter reset()
mettra simplement à jour l'enregistrement actuellement pointé par le mappeur.
Bien que le problème d'avoir des clés primaires dans toutes les tableaux de votre base de données soit argumentatif, F3 ne vous empêche pas de créer un objet de mappeur de données qui communique avec un tableau ne contenant aucune clé primaire. Le seul inconvénient est: vous ne pouvez pas supprimer ou mettre à jour un enregistrement mappé car il n'y a absolument aucun moyen pour F3 de déterminer quel enregistrement vous faites référence et le fait que les références de position ne sont pas fiables. Les ID de ligne ne sont pas portables sur différents moteurs SQL et ne peuvent pas être renvoyés par le pilote de base de données PHP.
Pour supprimer un enregistrement cartographié de notre tableau, invoquez la méthode erase()
sur un mappeur de données hydraté automatique. Par exemple:-
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID=? AND password=? ' , ' cheetah ' , ' ch1mp ' ]);
$ user -> erase ();
La syntaxe des requêtes du gabarit serait légèrement similaire: -
$ user = new DB Jig Mapper ( $ db , ' users ' );
$ user -> load ([ ' @userID=? AND @password=? ' , ' cheetah ' , ' chimp ' ]);
$ user -> erase ();
Et l'équivalent de MongoDB serait: -
$ user = new DB Mongo Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID ' => ' cheetah ' , ' password ' => ' chimp ' ]);
$ user -> erase ();
Pour savoir si notre mappeur de données était hydraté ou non: -
if ( $ user -> dry ())
echo ' No record matching criteria ' ;
Nous avons couvert les gestionnaires de crud. Il existe des méthodes supplémentaires que vous pourriez trouver utiles: -
$ f3 -> set ( ' user ' , new DB SQL Mapper ( $ db , ' users ' ));
$ f3 -> get ( ' user ' )-> copyFrom ( ' POST ' );
$ f3 -> get ( ' user ' )-> save ();
Notez que nous pouvons également utiliser des variables sans gras comme conteneurs pour les objets de mapper. La méthode copyFrom()
hydrate l'objet de mappeur avec des éléments d'une variable de tableau de framework, dont les touches de tableau doivent avoir des noms identiques aux propriétés de l'objet de mapper, qui à leur tour correspondent aux noms de champ de l'enregistrement. Ainsi, lorsqu'un formulaire Web est soumis (en supposant que l'attribut de nom HTML est défini sur userID
), le contenu de ce champ de saisie est transféré à $_POST['userID']
, dupliquée par F3 dans sa variable POST.userID
, et enregistrée à Le champ mappé $user->userID
dans la base de données. Le processus devient très simple s'ils ont tous des éléments nommés identiques. La cohérence dans les touches de tableau, les noms de jetons de modèle, c'est-à-dire, les noms de variables et les noms de champ Framework est clé :)
D'un autre côté, si nous voulions récupérer un enregistrement et copier les valeurs de champ dans une variable de framework pour une utilisation ultérieure, comme le rendu du modèle: -
$ f3 -> set ( ' user ' , new DB SQL Mapper ( $ db , ' users ' ));
$ f3 -> get ( ' user ' )-> load ([ ' userID=? ' , ' jane ' ]);
$ f3 -> get ( ' user ' )-> copyTo ( ' POST ' );
Nous pouvons ensuite attribuer {{@ post.userid}} à l'attribut de valeur du même champ de saisie. Pour résumer, le champ de saisie HTML ressemblera à ceci: -
< input type =" text " name =" userID " value =" {{ @POST.userID }} " />
Les méthodes save()
, update()
, copyFrom()
Data Mapper et les variantes paramétrées de load()
et erase()
sont à l'abri de l'injection SQL.
Par défaut, la méthode load()
d'un mappeur de données récupère uniquement le premier enregistrement qui correspond aux critères spécifiés. Si vous en avez plus d'un qui répond à la même condition que le premier enregistrement chargé, vous pouvez utiliser la méthode skip()
pour la navigation: -
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ( ' visits>3 ' );
// Rewritten as a parameterized query
$ user -> load ([ ' visits>? ' , 3 ]);
// For MongoDB users : -
// $ user = new DB Mongo Mapper ($ db , 'users' );
// $ user - > load ([ 'visits' = > [ '$gt' = > 3 ]]);
// If you prefer Jig : -
// $ user = new DB Jig Mapper ($ db , 'users' );
// $ user - > load ( '@visits>?' , 3 );
// Display the userID of the first record that matches the criteria
echo $ user -> userID ;
// Go to the next record that matches the same criteria
$ user -> skip (); // Same as $ user - > skip ( 1 );
// Back to the first record
$ user -> skip (- 1 );
// Move three records forward
$ user -> skip ( 3 );
Vous pouvez utiliser $user->next()
comme substitut pour $user->skip()
, et $user->prev()
si vous pensez qu'il donne plus de sens à $user->skip(-1)
.
Utilisez la méthode dry()
pour vérifier si vous avez manœuvré au-delà des limites de l'ensemble de résultats. dry()
reviendra True si vous essayez skip(-1)
sur le premier enregistrement. Il reviendra également vrai si vous skip(1)
sur le dernier record qui répond aux critères de récupération.
La méthode load()
accepte un deuxième argument: un tableau d'options contenant des paires de valeurs clés telles que: -
$ user -> load (
[ ' visits>? ' , 3 ],
[
' order ' => ' userID DESC '
'offset'=> 5 ,
' limit ' => 3
]
);
Si vous utilisez MySQL, la requête se traduit par: -
SELECT * FROM users
WHERE visits > 3
ORDER BY userID DESC
LIMIT 3 OFFSET 5 ;
C'est une façon de présenter des données en petits morceaux. Voici une autre façon de paginer les résultats: -
$ page = $ user -> paginate ( 2 , 5 ,[ ' visits>? ' , 3 ]);
Dans le scénario ci-dessus, F3 récupérera des enregistrements qui correspondent aux critères 'visits>3'
. Il limitera ensuite les résultats à 5 enregistrements (par page) à partir de la page Offset 2 (basée sur 0). Le cadre renverra un tableau composé des éléments suivants: -
[subset] array of mapper objects that match the criteria
[count] number of subsets available
[pos] actual subset position
La position de sous-ensemble réelle renvoyée sera nul si le premier argument de paginate()
est un nombre négatif ou dépasse le nombre de sous-ensembles trouvés.
Il y a des cas où vous devez récupérer une valeur calculée d'un champ, ou une valeur croisée à partir d'un autre tableau. Entrez les champs virtuels. Le mini-formulaire SQL vous permet de travailler sur des données dérivées des champs existants.
Supposons que nous ayons le tableau suivant défini comme: -
CREATE TABLE products
productID VARCHAR ( 30 ),
description VARCHAR ( 255 ),
supplierID VARCHAR ( 30 ),
unitprice DECIMAL ( 10 , 2 ),
quantity INT ,
PRIMARY KEY (productID)
);
Aucun champ totalprice
n'existe, nous pouvons donc dire au framework de demander à partir du moteur de la base de données le produit arithmétique des deux champs: -
$ item = new DB SQL Mapper ( $ db , ' products ' );
$ item -> totalprice = ' unitprice*quantity ' ;
$ item -> load ([ ' productID=:pid ' , ' :pid ' => ' apple ' ]);
echo $ item -> totalprice ;
L'extrait de code ci-dessus définit un champ virtuel appelé totalprice
qui est calculé en multipliant unitprice
par la quantity
. Le mappeur SQL enregistre cette règle / formule, donc lorsque le temps vient de récupérer l'enregistrement de la base de données, nous pouvons utiliser le champ virtuel comme un champ mappé ordinaire.
Vous pouvez avoir des champs virtuels plus complexes: -
$ item -> mostNumber = ' MAX(quantity) ' ;
$ item -> load ();
echo $ item -> mostNumber ;
Cette fois, le cadre récupère le produit avec la quantité la plus élevée (remarquez la méthode load()
ne définit aucun critère, donc tous les enregistrements du tableau seront traités). Bien sûr, le champ Virtual Field mostNumber
vous donnera toujours la bonne figure si vous souhaitez limiter l'expression à un groupe spécifique d'enregistrements qui correspondent aux critères spécifiés.
Vous pouvez également dériver une valeur d'un autre tableau: -
$ item -> supplierName =
' SELECT name FROM suppliers ' .
' WHERE products.supplierID=suppliers.supplierID ' ;
$ item -> load ();
echo $ item -> supplierName ;
Chaque fois que vous chargez un enregistrement de la table des produits, les références ORM croisent le supplerID
dans la table products
avec le supplierID
dans la table suppliers
.
Pour détruire un champ virtuel, utilisez unset($item->totalPrice);
. L'expression isset($item->totalPrice)
renvoie True si le champ virtuel totalPrice
a été défini, ou faux si autrement.
N'oubliez pas qu'un champ virtuel doit être défini avant la récupération des données. L'ORM n'effectue pas le calcul réel, ni la dérivation des résultats d'un autre tableau. C'est le moteur de base de données qui fait tout le travail acharné.
Si vous n'avez pas besoin de navigation enregistrée par record, vous pouvez récupérer un lot entier d'enregistrements en un seul coup: -
$ frequentUsers = $ user -> find ([ ' visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
La syntaxe des requêtes du gabarit de gabarit a une légère ressemblance: -
$ frequentUsers = $ user -> find ([ ' @visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
Le code équivalent utilisant le mappeur MongoDB: -
$ frequentUsers = $ user -> find ([ ' visits ' =>[ ' $gt ' => 3 ]],[ ' userID ' => 1 ]);
La méthode find()
recherche dans la table users
pour les enregistrements qui correspondent aux critères, trie le résultat par userID
et renvoie le résultat en tant que tableau d'objets mappeurs. find('visits>3')
est différent de load('visits>3')
. Ce dernier fait référence à l'objet $user
actuel. find()
n'a aucun effet sur skip()
.
IMPORTANT: Déclarer une condition vide, null ou une chaîne de longueur zéro comme le premier argument de find()
ou load()
récupérera tous les enregistrements. Assurez-vous de savoir ce que vous faites - vous pouvez dépasser la mémoire de PHP_limit sur de grandes tables ou collections.
La méthode find()
a la syntaxe suivante: -
find (
$ criteria ,
[
' group ' => ' foo ' ,
' order ' => ' foo,bar ' ,
' limit ' => 5 ,
' offset ' => 0
]
);
find () renvoie un tableau d'objets. Chaque objet est un mappeur à un enregistrement qui correspond aux critères spécifiés.:
$ place = new DB SQL Mapper ( $ db , ' places ' );
$ list = $ place -> find ( ' state="New York" ' );
foreach ( $ list as $ obj )
echo $ obj -> city . ' , ' . $ obj -> country ;
Si vous avez besoin de convertir un objet de mappeur en un tableau associatif, utilisez la méthode cast()
: -
$ array = $ place -> cast ();
echo $ array [ ' city ' ]. ' , ' . $ array [ ' country ' ];
Pour récupérer le nombre d'enregistrements dans un tableau qui correspond à une certaine condition, utilisez la méthode count()
.
if (! $ user -> count ([ ' visits>? ' , 10 ]))
echo ' We need a better ad campaign! ' ;
Il existe également une méthode select()
similaire à find()
mais qui fournit un contrôle plus fin sur les champs renvoyés. Il a une syntaxe de type SQL: -
select (
' foo, bar, MIN(baz) AS lowest ' ,
' foo > ? ' ,
[
' group ' => ' foo, bar ' ,
' order ' => ' baz ASC ' ,
' limit ' => 5 ,
' offset ' => 3
]
);
Tout comme la méthode find()
, select()
ne modifie pas le contenu de l'objet de mapper. Il ne sert que de méthode de commodité pour interroger une table mappée. La valeur de retour des deux méthodes est un tableau d'objets mappeurs. L'utilisation de dry()
pour déterminer si un enregistrement a été trouvé par ces méthodes est inapproprié. Si aucun enregistrement ne correspond aux critères find()
ou select()
, la valeur de retour est un tableau vide.
Si vous souhaitez savoir quelles instructions SQL émises directement par votre application (ou indirectement à travers des objets de mapper) provoquent des goulots d'étranglement de performance, vous pouvez le faire avec un simple: -
echo $ db -> log ();
F3 garde une trace de toutes les commandes émises au pilote de base de données SQL sous-jacent, ainsi que le temps nécessaire à chaque instruction - juste les bonnes informations dont vous avez besoin pour modifier les performances de l'application.
Dans la plupart des cas, vous pouvez vivre selon le confort donné par les méthodes de mappeur de données dont nous avons discuté jusqu'à présent. Si vous avez besoin du framework pour effectuer des travaux robustes, vous pouvez étendre le mappeur SQL en déclarant vos propres classes avec des méthodes personnalisées - mais vous ne pouvez pas éviter de mettre les mains grasses sur un SQL hardcore: -
class Vendor extends DB SQL Mapper {
// Instantiate mapper
function __construct ( DB SQL $ db ) {
// This is where the mapper and DB structure synchronization occurs
parent :: __construct ( $ db , ' vendors ' );
}
// Specialized query
function listByCity () {
return $ this -> select (
' vendorID,name,city ' ,[ ' order ' => ' city DESC ' ]);
/ *
We could have done the the same thing with plain vanilla SQL : -
return $ this - > db - > exec (
'SELECT vendorID,name,city FROM vendors ' .
'ORDER BY city DESC;'
);
* /
}
}
$ vendor = new Vendor ;
$ vendor -> listByCity ();
L'extension des mappeurs de données de cette manière est un moyen facile de construire des modèles liés à la DB de votre application.
Si vous êtes à portée de main avec SQL, vous diriez probablement: tout dans l'ORM peut être manipulé avec des requêtes SQL à l'ancienne. En effet. Nous pouvons nous passer des écouteurs d'événements supplémentaires en utilisant des déclencheurs de base de données et des procédures stockées. Nous pouvons accomplir des requêtes relationnelles avec des tables jointes. L'ORM n'est que des frais généraux inutiles. Mais le point est - les mappeurs de données vous donnent la fonctionnalité ajoutée de l'utilisation d'objets pour représenter les entités de base de données. En tant que développeur, vous pouvez écrire du code plus rapidement et être plus productif. Le programme résultant sera plus propre, sinon plus court. Mais vous devrez peser les avantages par rapport au compromis de la vitesse - spécialement lors de la gestion des magasins de données importants et complexes. N'oubliez pas que tous les orms - quelle que soit leur mince - ne seront toujours qu'une autre couche d'abstraction. Ils doivent encore passer le travail aux moteurs SQL sous-jacents.
Par conception, les ORM de F3 ne fournissent pas de méthodes pour connecter directement les objets les uns aux autres, c'est-à-dire les jointures SQL - car cela ouvre une boîte de vers. Cela rend votre application plus complexe qu'elle ne devrait l'être, et il y a la tendance des objets via des techniques de récupération avides ou paresseuses pour être bloqués et même hors synchronisation en raison de l'héritage des objets et du polymorphisme (décalage d'impédance) avec les entités de base de données qu'ils sont mappés pour . Il existe des moyens indirects de le faire dans le mappeur SQL, en utilisant des champs virtuels - mais vous devrez le faire par programme et à vos risques et périls.
Si vous êtes tenté d'appliquer des concepts OOP "purs" dans votre application pour représenter toutes vos données (car "tout est un objet"), gardez à l'esprit que les données vivent presque toujours plus longtemps que l'application. Votre programme peut déjà être dépassé bien avant que les données aient perdu sa valeur. N'ajoutez pas une autre couche de complexité dans votre programme en utilisant des objets entrelacés et des classes qui s'écartent trop du schéma et de la structure physique des données.
Avant de tisser plusieurs objets ensemble dans votre application pour manipuler les tables sous-jacentes de votre base de données, pensez-y: la création de vues pour représenter les relations et les déclencheurs pour définir le comportement des objets dans le moteur de la base de données est plus efficace. Les moteurs de la base de données relationnels sont conçus pour gérer les vues, les tables et les déclencheurs rejoints. Ce ne sont pas des magasins de données stupides. Les tables jointes dans une vue apparaîtront comme une seule table, et une vue sans gras peut également mapper une vue aussi bien qu'une table ordinaire. La réplication des jointures en tant qu'objets relationnels dans PHP est plus lente par rapport au code machine de la base de données, à l'algèbre relationnelle et à la logique d'optimisation. En outre, rejoindre des tables à plusieurs reprises dans notre application est un signe sûr que la conception de la base de données doit être vérifiée, et les vues sont considérées comme faisant partie intégrante de la récupération des données. Si une table traverse fréquemment les données d'un autre tableau, envisagez de normaliser vos structures ou de créer une vue à la place. Créez ensuite un objet de mappeur sur la carte automatique de cette vue. C'est plus rapide et nécessite moins d'efforts.
Considérez cette vue SQL créée à l'intérieur de votre moteur de base de données: -
CREATE VIEW combined AS
SELECT
projects . project_id AS project,
users . name AS name
FROM projects
LEFT OUTER JOIN users ON
projects . project_id = users . project_id AND
projects . user_id = users . user_id ;
Votre code d'application devient simple car il n'a pas à maintenir deux objets mappeurs (un pour la table des projets et un autre pour les utilisateurs) juste pour récupérer des données à partir de deux tables jointes: -
$ combined = new DB SQL Mapper ( $ db , ' combined ' );
$ combined -> load ([ ' project=? ' , 123 ]);
echo $ combined -> name ;
Astuce: utilisez les outils tels qu'ils sont conçus. Sans gras a déjà une aide SQL facile à utiliser. Utilisez-le si vous avez besoin d'un marteau plus grand :) Essayez de chercher un équilibre entre la commodité et les performances. SQL sera toujours votre secours si vous travaillez sur des structures de données complexes et héritées.
Plug-ins are nothing more than autoloaded classes that use framework built-ins to extend F3's features and functionality. If you'd like to contribute, leave a note at the Fat-Free Discussion Area hosted by Google Groups or tell us about it in the FreeNode #fatfree
IRC channel. Someone else might be involved in a similar project. The framework community will appreciate it a lot if we unify our efforts.
There might be instances when you want to make your forms more secure against spam bots and malicious automated scripts. F3 provides a captcha()
method to generate images with random text that are designed to be recognizable only by humans.
$ img = new Image ();
$ img -> captcha ( ' fonts/CoolFont.ttf ' , 16 , 5 , ' SESSION.captcha_code ' );
$ img -> render ();
This example generates an random image based on your desired TrueType font. The fonts/
folder is a subfolder within application's UI
path. The second parameter indicates the font size, and the third argument defines the number of hexadecimal characters to generate.
The last argument represents an F3 variable name. This is where F3 will store the string equivalent of the CAPTCHA image. To make the string reload-safe, we specified a session variable:- SESSION.captcha_code
which maps to $_SESSION['captcha_code']
, which you can use later to verify whether the input element in the form submitted matches this string.
We've covered almost every feature available in the framework to run a stand-alone Web server. For most applications, these features will serve you quite well. But what do you do if your application needs data from another Web server on the network? F3 has the Web plugin to help you in this situation:-
$ web = new Web ;
$ request = $ web -> request ( ' http://www.google.com/ ' );
// another way to do it : -
$ request =Web:: instance ()-> request ( ' http://www.google.com/ ' );
This simple example sends an HTTP request to the page located at www.google.com and stores it in the $request
PHP variable. The request()
method returns an array containing the HTTP response such that $request['headers']
and $request['body']
represent the response headers and body, respectively. We could have saved the contents using the F3::set command, or echo'ed the output directly to our browser. Retrieving another HTML page on the net may not have any practical purpose. But it can be particularly useful in ReSTful applications, like querying a CouchDB server.
$ host = ' localhost:5984 ' ;
$ web -> request ( $ host . ' /_all_dbs ' ),
$ web -> request ( $ host . ' /testdb/ ' ,[ ' method ' => ' PUT ' ]);
You may have noticed that you can pass an array of additional options to the request()
method:-
$ web -> request (
' https://www.example.com:443? ' .
http_build_query (
[
' key1 ' => ' value1 ' ,
' key2 ' => ' value2 '
]
),
[
' headers ' =>[
' Accept: text/html,application/xhtml+xml,application/xml ' ,
' Accept-Language: en-us '
],
' follow_location ' => FALSE ,
' max_redirects ' => 30 ,
' ignore_errors ' => TRUE
]
);
If the framework variable CACHE
is enabled, and if the remote server instructs your application to cache the response to the HTTP request, F3 will comply with the request and retrieve the cached response each time the framework receives a similar request from your application, thus behaving like a browser.
Fat-Free will use whatever means are available on your Web server for the request()
method to run: PHP stream wrappers ( allow_url_fopen
), cURL module, or low-level sockets.
F3 has a utility for sending files to an HTTP client, ie fulfilling download requests. You can use it to hide the real path to your download files. This adds some layer of security because users won't be able to download files if they don't know the file names and their locations. Here's how it's done:-
$ f3 -> route ( ' GET /downloads/@filename ' ,
function ( $ f3 , $ args ) {
// send () method returns FALSE if file doesn ' t exist
if (!Web:: instance ()-> send ( ' /real/path/ ' . $ args [ ' filename ' ]))
// Generate an HTTP 404
$ f3 -> error ( 404 );
}
);
The request()
method can also be used in complex SOAP or XML-RPC applications, if you find the need for another Web server to process data on your computer's behalf - thus harnessing the power of distributing computing. W3Schools.com has an excellent tutorial on SOAP. On the other hand, TutorialsPoint.com gives a nice overview of XML-RPC.
Caching static Web pages - so the code in some route handlers can be skipped and templates don't have to be reprocessed - is one way of reducing your Web server's work load so it can focus on other tasks. You can activate the framework's cache engine by providing a third argument to the $f3->route()
method. Just specify the number of seconds before a cached Web page expires:-
$ f3 -> route ( ' GET /my_page ' , ' App->method ' , 60 );
Voici comment cela fonctionne. In this example, when F3 detects that the URL /my_page
is accessed for the first time, it executes the route handler represented by the second argument and saves all browser output to the framework's built-in cache (server-side). A similar instruction is automatically sent to the user's Web browser (client-side), so that instead of sending an identical request to the server within the 60-second period, the browser can just retrieve the page locally. The framework uses the cache for an entirely different purpose - serving framework-cached data to other users asking for the same Web page within the 60-second time frame. It skips execution of the route handler and serves the previously-saved page directly from disk. When someone tries to access the same URL after the 60-second timer has lapsed, F3 will refresh the cache with a new copy.
Web pages with static data are the most likely candidates for caching. Fat-Free will not cache a Web page at a specified URL if the third argument in the $f3->route()
method is zero or unspecified. F3 conforms to the HTTP specifications: only GET and HEAD requests can be cached.
Here's an important point to consider when designing your application. Don't cache Web pages unless you understand the possible unwanted side-effects of the cache at the client-side. Make sure that you activate caching on Web pages that have nothing to do with the user's session state.
For example, you designed your site in such a way that all your Web pages have the menu options: "Home"
, "About Us"
, and "Login"
, displayed when a user is not logged into your application. You also want the menu options to change to: "Home"
, "About Us"
, and "Logout"
, once the user has logged in. If you instructed Fat-Free to cache the contents of "About Us"
page (which includes the menu options), it does so and also sends the same instruction to the HTTP client. Regardless of the user's session state, ie logged in or logged out, the user's browser will take a snapshot of the page at the session state it was in. Future requests by the user for the "About Us"
page before the cache timeout expires will display the same menu options available at that time the page was initially saved. Now, a user may have already logged in, but the menu options are still the same as if no such event occurred. That's not the kind of behavior we want from our application.
Some pointers:-
GET
routes only. It will not cache submitted forms!Don't activate the cache on Web pages that at first glance look static. In our example, the "About Us" content may be static, but the menu isn't."About Us"
page, make sure it's available only when a user is not logged in.CACHE
global variable so it points to that drive. This will make your application run like a Formula 1 race car. Note: Don't set the timeout value to a very long period until you're ready to roll out your application, ie the release or production state. Changes you make to any of your PHP scripts may not have the expected effect on the displayed output if the page exists in the framework cache and the expiration period has not lapsed. If you do alter a program that generates a page affected by the cache timer and you want these changes to take effect immediately, you should clear the cache by erasing the files in the cache/ directory (or whatever path the CACHE
global variable points to) . F3 will automatically refresh the cache if necessary. At the client-side, there's little you can do but instruct the user to clear the browser's cache or wait for the cache period to expire.
PHP needs to be set up correctly for the F3 cache engine to work properly. Your operating system timezone should be synchronized with the date.timezone setting in the php.ini
file.
Similar to routes, Fat-Free also allows you to cache database queries. Speed gains can be quite significant, specially when used on complex SQL statements that involve look-up of static data or database content that rarely changes. Activating the database query cache so the framework doesn't have to re-execute the SQL statements every time is as simple as adding a 3rd argument to the F3::sql command - the cache timeout. Par exemple:-
$ db -> exec ( ' SELECT * from sizes; ' , NULL , 86400 );
If we expect the result of this database query to always be Small
, Medium
, and Large
within a 24-hour period, we specify 86400
seconds as the 2nd argument so Fat-Free doesn't have to execute the query more than once a day . Instead, the framework will store the result in the cache, retrieve it from the cache every time a request comes in during the specified 24-hour time frame, and re-execute the query when the timer lapses.
The SQL data mapper also uses the cache engine to optimize synchronization of table structures with the objects that represent them. The default is 60
seconds. If you make any changes to a table's structure in your database engine, you'll have to wait for the cache timer to expire before seeing the effect in your application. You can change this behavior by specifying a third argument to the data mapper constructor. Set it to a high value if you don't expect to make any further changes to your table structure.
$ user = new DB SQL Mapper ( $ db , ' users ' , 86400 );
By default, Fat-Free's cache engine is disabled. You can enable it and allow it to auto-detect APC, WinCache or XCache. If it cannot find an appropriate backend, F3 will use the filesystem, ie the tmp/cache/
folder:-
$ f3 -> set ( ' CACHE ' , TRUE );
Disabling the cache is as simple as:-
$ f3 -> set ( ' CACHE ' , FALSE );
If you wish to override the auto-detection feature, you can do so - as in the case of a Memcached back-end which F3 also supports:-
$ f3 -> set ( ' CACHE ' , ' memcache=localhost:11211 ' );
You can also use the cache engine to store your own variables. These variables will persist between HTTP requests and remain in cache until the engine receives instructions to delete them. To save a value in the cache:-
$ f3 -> set ( ' var ' , ' I want this value saved ' , 90 );
$f3->set()
method's third argument instructs the framework to save the variable in the cache for a 90-second duration. If your application issues a $f3->get('var')
within this period, F3 will automatically retrieve the value from cache. In like manner, $f3->clear('var')
will purge the value from both cache and RAM. If you want to determine if a variable exists in cache, `$f3->exists('var')); returns one of two possible values: FALSE if the framework variable passed does not exist in cache, or an integer representing the time the variable was saved (Un*x time in seconds, with microsecond precision).
Fat-Free also has a Javascript and CSS compressor available in the Web plug-in. It can combine all your CSS files into one stylesheet (or Javascript files into a single script) so the number of components on a Web page are decreased. Reducing the number of HTTP requests to your Web server results in faster page loading. First you need to prepare your HTML template so it can take advantage of this feature. Something like:-
< link rel =" stylesheet " type =" text/css "
href =" /minify/css?files=typo.css,grid.css " />
Do the same with your Javascript files:-
< script type =" text/javascript " src =" /minify/js?&files=underscore.js " >
</ script >
Of course we need to set up a route so your application can handle the necessary call to the Fat-Free CSS/Javascript compressor:-
$ f3 -> route ( ' GET /minify/@type ' ,
function ( $ f3 , $ args ) {
$ f3 -> set ( ' UI ' , $ args [ ' type ' ]. ' / ' );
echo Web:: instance ()-> minify ( $ _GET [ ' files ' ]);
},
3600
);
And that's all there is to it! minify()
reads each file ( typo.css
and grid.css
in our CSS example, underscore.js
in our Javascript example), strips off all unnecessary whitespaces and comments, combines all of the related items as a single Web page component, and attaches a far-future expiry date so the user's Web browser can cache the data. It's important that the PARAMS.type
variable base points to the correct path. Otherwise, the URL rewriting mechanism inside the compressor won't find the CSS/Javascript files.
In our examples, the framework sends a far-future expiry date to the client's Web browser so any request for the same CSS or Javascript block will come from the user's hard drive. On the server side, F3 will check each request and see if the CSS or Javascript blocks have already been cached. The route we specified has a cache refresh period of 3600
seconds. Additionally, if the Web browser sends an If-Modified-Since
request header and the framework sees the cache hasn't changed, F3 just sends an HTTP 304 Not Modified
response so no content is actually delivered. Without the If-Modified-Since
header, Fat-Free renders the output from the cached file if available. Otherwise, the relevant code is executed.
Tip: If you're not modifying your Javascript/CSS files frequently (as it would be if you're using a Javascript library like jQuery, MooTools, Dojo, etc.), consider adding a cache timer to the route leading to your Javascript/CSS minify handler (3rd argument of F3::route()) so Fat-Free doesn't have compress and combine these files each time such a request is received.
Want to make your site run even faster? Fat-Free works best with either Alternative PHP Cache (APC), XCache, or WinCache. These PHP extensions boost performance of your application by optimizing your PHP scripts (including the framework code).
A fast application that processes all HTTP requests and responds to them at the shortest time possible is not always a good idea - specially if your bandwidth is limited or traffic on your Web site is particularly heavy. Serving pages ASAP also makes your application vulnerable to Denial-of-Service (DOS) attacks. F3 has a bandwidth throttling feature that allows you to control how fast your Web pages are served. You can specify how much time it should take to process a request:-
$ f3 -> route ( ' /throttledpage ' , ' MyApp->handler ' , 0 , 128 );
In this example, the framework will serve the Web page at a rate of 128KiBps.
Bandwidth throttling at the application level can be particularly useful for login pages. Slow responses to dictionary attacks is a good way of mitigating this kind of security risk.
Robust applications are the result of comprehensive testing. Verifying that each part of your program conforms to the specifications and lives up to the expectations of the end-user means finding bugs and fixing them as early as possible in the application development cycle.
If you know little or nothing about unit testing methodologies, you're probably embedding pieces of code directly in your existing program to help you with debugging. That of course means you have to remove them once the program is running. Leftover code fragments, poor design and faulty implementation can creep up as bugs when you roll out your application later.
F3 makes it easy for you to debug programs - without getting in the way of your regular thought processes. The framework does not require you to build complex OOP classes, heavy test structures, and obtrusive procedures.
A unit (or test fixture) can be a function/method or a class. Let's have a simple example:-
function hello () {
return ' Hello, World ' ;
}
Save it in a file called hello.php
. Now how do we know it really runs as expected? Let's create our test procedure:-
$ f3 = require ( ' lib/base.php ' );
// Set up
$ test = new Test ;
include ( ' hello.php ' );
// This is where the tests begin
$ test -> expect (
is_callable ( ' hello ' ),
' hello() is a function '
);
// Another test
$ hello = hello ();
$ test -> expect (
! empty ( $ hello ),
' Something was returned '
);
// This test should succeed
$ test ->expect
is_string ( $ hello ),
' Return value is a string '
);
// This test is bound to fail
$ test -> expect (
strlen ( $ hello )== 13 ,
' String length is 13 '
);
// Display the results ; not MVC but let ' s keep it simple
foreach ( $ test -> results () as $ result ) {
echo $ result [ ' text ' ]. ' <br /> ' ;
if ( $ result [ ' status ' ])
echo ' Pass ' ;
else
echo ' Fail ( ' . $ result [ ' source ' ]. ' ) ' ;
echo ' <br /> ' ;
}
Save it in a file called test.php
. This way we can preserve the integrity of hello.php
.
Now here's the meat of our unit testing process.
F3's built-in Test
class keeps track of the result of each expect()
call. The output of $test->results()
is an array of arrays with the keys text
(mirroring argument 2 of expect()
), status
(boolean representing the result of a test), and source
(file name/line number of the specific test) to aid in debugging.
Fat-Free gives you the freedom to display test results in any way you want. You can have the output in plain text or even a nice-looking HTML template. So how do we run our unit test? If you saved test.php
in the document root folder, you can just open your browser and specify the address http://localhost/test.php
. C'est tout ce qu'il y a à dire.
F3 gives you the ability to simulate HTTP requests from within your PHP program so you can test the behavior of a particular route. Here's a simple mock request:-
$ f3 -> mock ( ' GET /test?foo=bar ' );
To mock a POST request and submit a simulated HTML form:-
$ f3 -> mock ( ' POST /test ' ,[ ' foo ' => ' bar ' ]);
Once you get the hang of testing the smallest units of your application, you can then move on to the bigger components, modules, and subsystems - checking along the way if the parts are correctly communicating with each other. Testing manageable chunks of code leads to more reliable programs that work as you expect, and weaves the testing process into the fabric of your development cycle. The question to ask yourself is:- Have I tested all possible scenarios? More often than not, those situations that have not been taken into consideration are the likely causes of bugs. Unit testing helps a lot in minimizing these occurrences. Even a few tests on each fixture can greatly reduce headaches. On the other hand, writing applications without unit testing at all invites trouble.
string AGENT
Mozilla/5.0 (Linux; Android 4.2.2; Nexus 7) AppleWebKit/537.31
. bool AJAX
TRUE
if an XML HTTP request is detected, FALSE
otherwise. string AUTOLOAD
|
), comma ( ,
), or semi-colon ( ;
) as path separator. string BASE
index.php
main/front controller. string BODY
bool/string CACHE
'memcache=localhost'
(and the PHP memcache module is present), F3 auto-detects the presence of APC, WinCache and XCache and uses the first available PHP module if set to TRUE. If none of these PHP modules are available, a filesystem-based backend is used (default directory: tmp/cache
). The framework disables the cache engine if assigned a FALSE
value. bool CASELESS
FALSE
to make it case-sensitive. array COOKIE, GET, POST, REQUEST, SESSION, FILES, SERVER, ENV
integer DEBUG
string DNSBL
403 Forbidden
error if the user's IPv4 address is listed on the specified server(s). array DIACRITICS
string ENCODING
UTF-8
. array ERROR
ERROR.code
is the HTTP status code. ERROR.status
contains a brief description of the error. ERROR.text
provides more detail. For HTTP 500 errors, use ERROR.trace
to retrieve the stack trace. bool ESCAPE
string EXEMPT
string FALLBACK
bool HALT
array HEADERS
bool HIGHLIGHT
TRUE
(requires code.css
stylesheet). string HOST
$_SERVER['SERVER_NAME']
is not available, return value of gethostname()
is used. string IP
array JAR
string LANGUAGE
LOCALES
. If set to NULL
, language is auto-detected from the HTTP Accept-Language
request header. string LOCALES
string LOGS
mixed ONERROR
string PACKAGE
array PARAMS
route()
pattern. PARAMS.0
contains the captured URL relative to the Web root. string PATTERN
string PLUGINS
base.php
. int PORT
string PREFIX
bool QUIET
bool RAW
BODY
. Should be TRUE when processing large data coming from php://input
which will not fit in memory. Default value: FALSE
string REALM
string RESPONSE
QUIET
setting. string ROOT
array ROUTES
string SCHEME
http
or https
. string SERIALIZER
php
, unless PHP igbinary
extension is auto-detected. Assign json
if desired. string TEMP
tmp/
folder inside the Web root. Adjust accordingly to conform to your site's security policies. string TZ
date_default_timezone_set()
function. string UI
View
and Template
classes' render()
method. Default value is the Web root. Accepts a pipe ( |
), comma ( ,
), or semi-colon ( ;
) as separator for multiple paths. callback UNLOAD
string UPLOADS
string URI
string VERB
string VERSION
@token
@token
with value of equivalent F3 variable. {{ mixed expr }}
expr
may include template tokens, constants, operators (unary, arithmetic, ternary and relational), parentheses, data type converters, and functions. If not an attribute of a template directive, result is echoed. {{ string expr | raw }}
expr
. F3 auto-escapes strings by default. {{ string expr | esc }}
expr
. This is the default framework behavior. The | esc
suffix is only necessary if ESCAPE
global variable is set to FALSE
. {{ string expr, arg1, ..., argN | format }}
expr
and pass the comma-separated arguments, where arg1, ..., argn
is one of:- 'date'
, 'time'
, 'number, integer'
, 'number, currency'
, or 'number, percent'
. <include
[ if="{{ bool condition }}" ]
href="{{ string subtemplate }}"
/>
subtemplate
and insert at current position in template if optional condition is TRUE
. <exclude>text-block</exclude>
text-block
at runtime. Used for embedding comments in templates. <ignore>text-block</ignore>
text-block
as-is, without interpretation/modification by the template engine. <check if="{{ bool condition }}">
<true>true-block</true>
<false>false-block</false>
</check>
TRUE
, then true-block
is rendered. Otherwise, false-block
is used. <loop
from="{{ statement }}"
to="{{ bool expr }}"
[ step="{{ statement }}" ]>
text-block
</loop>
from
statement once. Check if the expression in the to
attribute is TRUE
, render text-block
and evaluate step
statement. Repeat iteration until to
expression is FALSE
. <repeat
group="{{ array @group|expr }}"
[ key="{{ scalar @key }}" ]
value="{{ mixed @value }}"
[ counter="{{ scalar @key }}" ]>
text-block
</repeat>
text-block
as many times as there are elements in the array variable @group
or the expression expr
. @key
and @value
function in the same manner as the key-value pair in the equivalent PHP foreach()
statement. Variable represented by key
in counter
attribute increments by 1
with every iteration. <switch expr="{{ scalar expr }}">
<case value="{{ scalar @value|expr }}" break="{{ bool TRUE|FALSE }}">
text-block
</case>
.
.
.
</switch>
{* text-block *}
<exclude>
.The most up-to-date documentation is located at http://fatfreeframework.com/. It contains examples of usage of the various framework components.
Technical support is available at the official discussion forum: https://groups.google.com/forum/#!forum/f3-framework
. If you need live support, you can talk to the development team and other members of the F3 community via Slack or IRC. We're on the FreeNode #fatfree
channel ( chat.freenode.net
). Visit http://webchat.freenode.net/
to join the conversation. You can also download the Firefox Chatzilla add-on or Pidgin if you don't have an IRC client so you can participate in the live chat. You can also find help at Stack Overflow
F3 uses Git for version control. To clone the latest code repository on GitHub:
git clone git://github.com/bcosca/fatfree-core.git
If all you want is a zipball of our test bench with all unit tests, grab it here .
To file a bug report, visit https://github.com/bcosca/fatfree-core/issues
.
Fat-Free Framework is free and released as open source software covered by the terms of the GNU Public License (GPL v3). You may not use the software, documentation, and samples except in compliance with the license. If the terms and conditions of this license are too restrictive for your use, alternative licensing is available for a very reasonable fee.
If you feel that this software is one great weapon to have in your programming arsenal, it saves you a lot of time and money, use it for commercial gain or in your business organization, please consider making a donation to the project. A significant amount of time, effort, and money has been spent on this project. Your donations help keep this project alive and the development team motivated. Donors and sponsors get priority support (24-hour response time on business days).
The Fat-Free Framework is community-driven software. It can't be what it is today without the help and support from the following people and organizations:
Special thanks to the selfless others who expressed their desire to remain anonymous, yet share their time, contribute code, send donations, promote the framework to a wider audience, as well as provide encouragement and regular financial assistance. Their generosity is F3's prime motivation.
By making a donation to this project you signify that you acknowledged, understood, accepted, and agreed to the terms and conditions contained in this notice. Your donation to the Fat-Free Framework project is voluntary and is not a fee for any services, goods, or advantages, and making a donation to the project does not entitle you to any services, goods, or advantages. We have the right to use the money you donate to the Fat-Free Framework project in any lawful way and for any lawful purpose we see fit and we are not obligated to disclose the way and purpose to any party unless required by applicable law. Although Fat-Free Framework is free software, to our best knowledge this project does not have any tax-exempt status. The Fat-Free Framework project is neither a registered non-profit corporation nor a registered charity in any country. Your donation may or may not be tax-deductible; please consult this with your tax advisor. We will not publish/disclose your name and e-mail address without your consent, unless required by applicable law. Your donation is non-refundable.
Copyright (c) 2009-2022 F3::Factory/Bong Cosca <[email protected]>
Hé mec ! Aide-moi pour quelques-uns !