I. Avant-propos▲
Il n'est pas possible à mon sens de parler de Silex sans faire allusion à Symfony2.
Alors de mon point de vue, je n'aime pas (encore) Symfony2.
Une des raisons est que je trouve que la prise en main de Symfony2 (SF2) est malaisée de prime abord.
Et c'est là qu'intervient Silex, ce (mini) framework, va être pour nous une porte d'entrée pour appréhender SF2.
I-A. Présentation▲
Silex est un framework PHP pour 5,3 PHP, inspiré du framework développé par Sensio Labs Symfony2.
Celui-ci a été créé par Fabien Potencier, père du framework Symfony, et Igor Wiedler, ingénieur logiciel. Il est publié sous licence MIT.
Silex est composé de Silex-Core, Twig qui est le template et certains composants de Symfony.
Silex utilise » Composer » :Composer est un outil moderne qui permet de gérer les dépendances de ses projets de manière simple et puissante.
Comme Silex est compilant 'psr-0', cela nous permet d'intégrer des bibliothèques externes très facilement.
I-B. Notre façon de travailler.▲
Il existe plusieurs façons de traiter et travailler avec Silex. Notamment Grégoire Pineau qui propose sa façon de faire, ou encore Mparaiso qui nous propose le fameux blog, c'est sur ce dernier que je me suis largement inspiré pour cet article.
Dans le projet que je dois mettre en place, il faut que cela soit maintenable, clair et précis.
Je mettrai donc en place une structure MVC (Model-View-Controller), et je vais distinguer deux parties. La première sera la mise en place de la structure et architecture de façon à disposer d'un outil prêt à l'emploi.
Cet outil se présentera en une arborescence définie, la mise en place de Silex, Twig et le bootstrap Twitter.
II. ▲
II-A. Arborescence▲
Notre structure du nouveau projet SilexSkeleton va ressembler à ceci :
II-B. Composer▲
À partir d'ici nous allons utiliser Composer pour gérer les dépendances de notre application, nous devons donc rapatrier cet outil.
Il existe plusieurs façons de faire et il existe plusieurs façons de l'utiliser, je vous laisse le soin de choisir selon votre préférence.
Une fois "Composer" installé, nous allons à la racine créer notre fichier de configuration JSON, qui va nous permettre de gérer nos dépendances.
Éditons un fichier nommer composer.json et plaçons-le à la racine de notre projet.
{
"
minimum-stability
"
:
"
dev
"
,
"
require
"
:{
"
silex/silex
"
:
"
1.0.*
"
}
}
Dans ce fichier nous demandons d'installer Silex version 1.0.*, l'astérisque représentant les sous-branches, mais avec un minimum de stabilité.
Lançons l'installation :
php composer.
phar install
Ce qui chez moi donne ceci :
This dev build of composer is outdated,
please run "
composer.phar self-update
"
to get the latest version.
Installing dependencies
-
Installing pimple/
pimple (dev-
master)
Cloning b9f27b8dc18c08f00627dec02359b46a24791dc3
-
Installing symfony/
routing (2.1
.
x-
dev)
Cloning v2.
1.2
-
Installing symfony/
http-
foundation (2.1
.
x-
dev)
Cloning 17
af0b966d268aa62e3487e3c48f0742e919edd1
-
Installing symfony/
event-
dispatcher (2.1
.
x-
dev)
Cloning v2.
1.2
-
Installing symfony/
http-
kernel (2.1
.
x-
dev)
Cloning 9
d00c6069a5e194826091d72baa5402f623ebf31
-
Installing silex/
silex (dev-
master)
Cloning fc82b4255ef081dd7d464c5320ca93a902f9190f
symfony/
routing suggests installing doctrine/
common (=
2.2
,
2.4
-
dev)
symfony/
routing suggests installing symfony/
config (2.1
.*
)
symfony/
routing suggests installing symfony/
yaml (2.1
.*
)
symfony/
event-
dispatcher suggests installing symfony/
dependency-
injection (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
browser-
kit (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
class-
loader (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
config (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
console (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
dependency-
injection (2.1
.*
)
symfony/
http-
kernel suggests installing symfony/
finder (2.1
.*
)
silex/
silex suggests installing symfony/
browser-
kit (2.1
.*
)
silex/
silex suggests installing symfony/
css-
selector (2.1
.*
)
silex/
silex suggests installing symfony/
dom-
crawler (2.1
.*
)
Writing lock file
Generating autoload files
Et où l'on retrouve un nouveau répertoire vendor/
II-C. Autoloader▲
Ce qui est intéressant à regarder dans tous ces nouveaux fichiers et répertoires, c'est l'autoloader qui est pris en charge intégralement par "Composer".
II-C-1. vendor/composer/autoload_namespaces.php▲
<?php
// autoload_namespaces.php generated by Composer
$vendorDir
=
dirname(__DIR__);
$baseDir
=
dirname($vendorDir
);
return
array
(
'Symfony\\Component\\Routing'
=>
$vendorDir
.
'/symfony/routing/'
,
'Symfony\\Component\\HttpKernel'
=>
$vendorDir
.
'/symfony/http-kernel/'
,
'Symfony\\Component\\HttpFoundation'
=>
$vendorDir
.
'/symfony/http-foundation/'
,
'Symfony\\Component\\EventDispatcher'
=>
$vendorDir
.
'/symfony/event-dispatcher/'
,
'Silex'
=>
$vendorDir
.
'/silex/silex/src/'
,
'SessionHandlerInterface'
=>
$vendorDir
.
'/symfony/http-foundation/Symfony/Component/HttpFoundation/Resources/stubs'
,
'Pimple'
=>
$vendorDir
.
'/pimple/pimple/lib/'
,
);
III. ▲
Nous allons maintenant pouvoir commencer notre projet, en continuant sa mise en place.
III-A. index.php▲
Dans le répertoire public, créons notre fichier d'entrée le "index.php" et faisons-le pointer sur le fichier structure de notre application.
<?php
require_once __DIR__ .
'/../App/bootstrap.php'
;
Et tant que nous y sommes, créons le fichier .htaccess (pour Apache) qui va bien :
<
IfModule mod_rewrite>
Options
-
MultiViews
RewriteEngine On
#RewriteBase /path/to/app
RewriteCond %{
REQUEST_FILENAME}
!-
f
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^
index.
php [
L]
</
IfModule>
III-B. bootstrap.php▲
Nous avons vu dans le index.php, que nous faisions appel à un fichier de configuration nommé bootsrap.php dans notre répertoire, c'est dans ce fichier que nous allons mettre en place la mécanique pour faire fonctionner le framework.
Construisons-le pas à pas.
<?php
//On initialise le timeZone
ini_set('date.timezone'
,
'Europe/Brussels'
);
//On ajoute l'autoloader
$loader
=
require_once __DIR__ .
'/../vendor/autoload.php'
;
//dans l'autoloader nous ajoutons notre répertoire applicatif
$loader
->
add("App"
,
dirname(__DIR__));
//Nous instancions un objet Silex\Application
$app
=
new
Silex\Application();
//en dev, nous voulons voir les erreurs
$app
[
'debug'
]
=
true
;
//On lance l'application
$app
->
run();
Il ne nous reste plus qu'à lancer notre appli sur http://localhost/SilexApplication/public/
Nous tombons sur une belle erreur.
Cette erreur prouve que notre application est bien configurée.
Silex nous réclame une route, normal, il dit « je vais où moi ? » et dans ce que vous pourrez trouver sur le net, à partir d'ici les façons de faire divergent.
Dans notre cas, on va dire au boostrap où allez voir, dans quel contrôleur nous allons.
C'est dans le(s) contrôleur(s) que nous allons définir nos routes.
Dans le bootsrap.php ajoutons la ligne suivante :
....
//On indique où allez pour le chemin http://localhost/SilexSkeleton/public/
$app
->
mount("
/
"
,
new App\Controller\IndexController());
//On lance l'application
$app
->
run();
III-C. App/Controller/IndexController.php▲
Dans notre IndexController.php, nous allons définir trois méthodes (juste pour l'exemple) :
- index ;
- info ;
- connect.
C'est dans la méthode connect que nous définirons nos routes.
Soit dans le fichier IndexController.php, définissons notre namespace :
<?php
namespace
App\Controller {
}
Ajoutons les namespaces dont nous avons besoin pour faire prendre le tout.
<?php
namespace
App\Controller {
use
Silex\Application;
use
Silex\ControllerProviderInterface;
}
Créons notre classe étendue de ControllerProviderInterface :
<?php
namespace
App\Controller {
use
Silex\Application;
use
Silex\ControllerProviderInterface;
class
IndexController implements
ControllerProviderInterface
{
}
}
Nous allons écrire notre première méthode, qui va définir nos routes.
Il est bon de savoir que dans chaque contrôleur c'est la méthode connect() qui définira vos routes, donc soit vous la définissez une fois dans IndexController, soit vous la définissez dans chaque contrôleur, ce qui est l'option que j'ai prise.
public function connect
(Application $app
)
{
// créer un nouveau controller basé sur la route par défaut
$index
=
$app
[
'
controllers_factory
'
];
$index
->
match("
/
"
,
'
App\Controller\IndexController::index
'
);
$index
->
match("
/info
"
,
'
App\Controller\IndexController::info
'
);
return $index
;
}
Dans cette méthode qui prend en paramètre l'application Silex, nous récupérons la factory des contrôleurs et lui ajoutons deux routes :
- http://localhost/SilexSkeleton/public/ ;
- http://localhost/SilexSkeleton/public/info.
Ces deux routes font appel à deux méthodes de notre IndexController.
Définissons ces deux méthodes.
public function index()
{
return '
Bonjour
'
;
}
public function info()
{
return phpinfo();
}
Ce qui nous donne le contrôleur complet suivant :
<?php
namespace
App\Controller {
use
Silex\Application;
use
Silex\ControllerProviderInterface;
class
IndexController implements
ControllerProviderInterface
{
public
function
index()
{
return
'Bonjour'
;
}
public
function
info()
{
return
phpinfo();
}
public
function
connect(Application $app
)
{
// créer un nouveau controller basé sur la route par défaut
$index
=
$app
[
'controllers_factory'
];
$index
->
match("/"
,
'App\Controller\IndexController::index'
);
$index
->
match("/index"
,
'App\Controller\IndexController::index'
);
$index
->
match("/info"
,
'App\Controller\IndexController::info'
);
return
$index
;
}
}
}
Maintenant, si nous allons sur le lien suivant http://localhost/SilexSkeleton/public, cela affichera « bonjour ».
Tandis que le lien http://localhost/SilexSkeleton/public/info, vous affichera le phpinfo().
III-D. Conclusion▲
Potentiellement l'application est fonctionnelle en l'état et vous n'avez besoin de rien d'autre pour développer votre application.
Mais nous allons encore faire un petit pas plus loin.
IV. ▲
Twig est un moteur de templates PHP permettant de séparer la couche de présentation de vos applications web, tout en gardant flexibilité, rapidité et facilité au développement.
L'objectif principal de Twig est de proposer aux développeurs de séparer la couche de présentation (Vue du MVC) dans des templates dédiés, afin de favoriser la maintenabilité du code. Idéal aussi pour les graphistes qui ne connaissent pas forcément le langage PHP et qui s'accommoderont parfaitement des instructions natives du moteur, relativement simples à maîtriser.
Vous trouverez toute la doc et des exemples sur leur site.
IV-A. Installation▲
Pour installer Twig, nous allons utiliser "Composer", mais vous devez savoir que SensioLabs a également compilé en C cette bibliothèque et que donc on peut l'intégrer nativement à PHP (voir ici), cette compilation permettra aux très gros projets de gagner de la vélocité.
Installation de Twig, d'abord modifier le fichier composer.json, pour ajouter Twig :
{
"
minimum-stability
"
:
"
dev
"
,
"
require
"
:{
"
silex/silex
"
:
"
1.0.*
"
,
"
symfony/twig-bridge
"
:
"
2.1.*
"
,
"
twig/twig
"
:
"
>=1.8,<2.0-dev
"
}
}
Suivi de la commande :
php composer.
phar update
IV-B. bootstrap.php▲
Dans le boostrap, nous allons initialiser Twig, en donnant comme paramètre le répertoire par défaut et le cache.
/* twig */
$app
->
register(new Silex\Provider\TwigServiceProvider(),
array(
"
twig.path
"
=>
dirname(__DIR__) .
"
/App/Views
"
,
'
twig.options
'
=>
array('
cache
'
=>
dirname(__DIR__).
'
/cache
'
,
'
strict_variables
'
=>
true)
));
On voit bien ici, on dit à Twig d'aller chercher les vues dans /App/Views.
IV-C. layout.twig▲
Dans App/Views, créez un fichier layout.twig qui sera la vue de base de notre application et dedans nous aurons ceci :
{# layout principal #}
<!
doctype html
>
<html lang
=
"fr-FR"
>
<head>
<meta charset
=
"UTF-8"
>
<title>SilexSkeleton</title>
</head>
<body>
<header>
<div id
=
"content"
>
{% block content %}
{% endblock %}
</div>
</header>
<footer>
</footer>
</body>
</html>
Bon actuellement cela ne change rien, mais revenons dans notre IndexController et modifions quelque peu notre méthode index.
IV-D. IndexController.php▲
public function index(Application $app
)
{
return $app
[
"
twig
"
]->
render("
index/index.twig
"
);
}
Ici nous avons modifié deux choses, nous avons d'abord mis l'application en paramètre afin de pouvoir rechercher Twig.
Ensuite, nous avons donné le chemin d'une vue dans la méthode render, ce chemin est App/Views/index/index.twig.
IV-E. index.twig▲
Il suffit dans ce fichier de mettre le code suivant :
{%
extends "
layout.twig
"
%}
{%
block content %}
Bonjour !
de ma vue Twig
{%
endblock %}
Concrètement si vous allez sur le lien http://localhost/SiteSilex/public, cela ne change pas grand-chose, mais si vous regardez le code source :
<!
doctype html
>
<html lang
=
"fr-FR"
>
<head>
<meta charset
=
"UTF-8"
>
<title>SilexSkeleton</title>
</head>
<body>
<header>
<div id
=
"content"
>
Bonjour ! de ma vue Twig
</div>
</header>
<footer>
</footer>
</body>
</html>
Nous avons bien une page HTML complète, je vous renvoie sur la doc de Twig pour comprendre la gestion des 'extends' et 'block' de Twig.
V. ▲
V-A. Mise en page▲
Ici nous allons mettre en place le Bootstrap de Twitter, pourquoi ? Juste pour pouvoir avoir un peu de mise en page et puis c'est une bonne excuse pour parler de la génération de routes.
V-A-1. Routes▲
Dans notre controller, nous avons généré les routes dans la méthode connect(), mais nous allons "binder" ces routes et les récupérer dans d'autres contextes.
V-A-2. bootstrap.php▲
Il faut ajouter la ligne suivante dans le bootstrap.php :
# url generator
$app
->
register(new Silex\Provider\UrlGeneratorServiceProvider());
Grâce à ce provider, nous allons pouvoir utiliser deux méthodes :
- url() ;
- path().
Mais avant nous devons "binder" les routes dans notre controller
public function connect
(Application $app
)
{
// créer un nouveau controller basé sur la route par défaut
$index
=
$app
[
'
controllers_factory
'
];
$index
->
match("
/
"
,
'
App\Controller\IndexController::index
'
)->
bind("
index.index
"
);
$index
->
match("
/info
"
,
'
App\Controller\IndexController::info
'
)->
bind("
index.info
"
);
return $index
;
}
Il ne reste plus qu'à télécharger le bootstrap Twitter et l'installer dans /public, je vous mets également une petite css perso pour finaliser :
body
{
margin-top:
70
px;
}
html
{
position:
relative
;
}
body
{
font:
11
px Verdana,
Arial,
Helvetica,
sans-serif
;
background-color:
#efefef
;
/* margin:1px;
padding:0px;*/
}
footer
{
background-color:
#F5F5F5
;
border-top:
1
px solid
#E5E5E5
;
margin-top:
70
px;
padding:
70
px 0
;
}
.pull-center
{
text-align:
center
;
}
#content
{
width:
75
%;
margin-left:
auto
;
margin-right:
auto
;
padding:
5
px;
border:
1
px solid
#cccccc
;
margin-top:
20
px;
background-color:
white
;
position:
relative
;
z-index:
3
;
}
#content
form label
{
display:
block
;
width:
300
px;
background-color:
#B9ED5D
;
float:
left
;
text-align:
right
;
line-height:
2.1
em;
padding:
0
0.8
em 0
0
;
margin-right:
3
px;
}
#content
form input
{
border:
1
px solid
#cccccc
;
width:
250
px;
-webkit-border-radius:
3
px;
-moz-border-radius:
3
px;
border-radius:
3
px;
}
#content
form input[
type=
checkbox]
{
display:
inline
;
width:
15
px;
}
V-B. layout.twig▲
Je vous mets ici le code avec l'intégration du bootstrap Twitter, regardez de plus près les méthodes path() et url() :
{# layout principal #}
<!
doctype html
>
<html lang
=
"fr-FR"
>
<head>
<meta charset
=
"UTF-8"
>
<title>SilexSkeleton</title>
<link rel
=
"stylesheet"
href
=
"{{ url('index.index') }}bootstrap/css/bootstrap.css"
>
<link rel
=
"stylesheet"
href
=
"{{ url('index.index') }}bootstrap/css/bootstrap-responsive.css"
>
<link rel
=
"stylesheet"
href
=
"{{ url('index.index') }}bootstrap/css/bootstrap-notify.css"
>
<link rel
=
"stylesheet"
href
=
"{{ url('index.index') }}css/default.css"
>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap.js"
></script>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap-dropdown.js"
></script>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap-transition.js"
></script>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap-scrollspy.js"
></script>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap-modal.js"
></script>
<script type
=
"text/javascript"
src
=
"{{ url('index.index') }}bootstrap/js/bootstrap-tab.js"
></script>
</head>
<body>
<header>
<body data-spy
=
"scroll"
data-target
=
".navbar"
>
<div class
=
"navbar navbar-inverse navbar-fixed-top"
>
<div class
=
"navbar-inner"
><section id
=
"I-F"
noNumber
=
"1"
>
<div class
=
"container"
>
<a class
=
"brand"
href
=
"#"
>
SilexSkeleton</a>
<div class
=
"nav-collapse collapse"
>
<p class
=
"navbar-text pull-right"
>
<b>Nous sommes le {{ "now"|date("d/m/Y") }} </b>
</p>
<ul class
=
"nav"
role
=
"navigation"
>
<li><a href
=
"{{ path("
index.index") }}"
>
Accueil</a></li>
<li><a href
=
"{{ path("
index.info") }}"
>
Info</a></li>
</ul>
</div>
</div>
</div>
</div>
</header>
<div id
=
"content"
>
{% block content %}
{% endblock %}
</div>
<footer>
</footer>
</body>
</html>
VI. La suite....▲
C'est ici que se termine cet article sur Silex.
J'espère que je vous ai montré avec quelle facilité il est possible de monter un projet en Silex, prochainement je développerai cet article avec les notions de Session, Base de données, Formulaire et Validation.
Ce qui est bien avec Silex, c'est qu'il est une bonne porte dentrée vers Symfony2, parce qu'inévitablement, plus le projet Silex est gros, plus on lorgne vers Symfony.
VII. Remerciement▲
Je profite de l'espace qui m'est donné pour remercier ClaudeLELOUPpour sa relecture. Ainsi que grunk pour ses remarques judicieuses.