Composer et Symfony

Nous allons à présent nous familiariser avec les outils et composants d’un Framework de référence: Symfony qui est très modulaire et permet d’installer des composants très riches comme SwiftMailer pour envoyer des mails, FOSUserBundle pour gérer des utilisateurs, FOSRESTBundle ou APIPlatform pour réaliser rapidement une API complète. Le Framework Symfony 4 est basé sur un Micro-noyau (Micro-Kernel) 70% plus léger que le noyau de Symfony 3. La version stable actuelle est la 5.1.7 sortie en Mai 2020.

Une introduction générale à ce framework se trouve ici

La gestion des dépendances se fait à présent grâce à l’outil Symfony Flex qui permet d’établir des recettes ou recipes décrivant les dépendances et la configuration d’un projet. L’outil de base est toujours composer.

Indication

Composer permet d’installer des centaines de packages librement disponibles. On les trouve sur Packagist . Il permet de gérer les dépendances d’un projet et également de créer le squellette d’une application Symfony et également d’installer des recettes flex (recipes) comme composer require mailer pour installer SwiftMailer ou composer require api pour installer APIPlatform avec toutes ses dépendances.

Si on veut juste installer un composant simple comme HTTPFoundation, on place à la racine du dossier de travail le fichier composer.json suivant :

{
"require": {
    "symfony/http-foundation": "~5.1"
    }
}

Ceci indique que nous n’installons pour l’instant que ce seul composant et que nous demandons la dernière version stable de la branche 4 pour http-foundation. Puis utilisons composer pour installer les composants demandés:

composer update

ou si on veut optimiser (à utiliser avec précaution) :

composer update -o

Indication

Notez l’utilisation de l’option -o de composer pour optimize-autoloader qui optimise « au mieux » le chargement automatique des classes. Cette optimisation peut s’avérer longue et parfois poser des problèmes. Ne pas l’utiliser en debuggage !

On peut aussi ne pas se fatiguer à écrire le fichier composer.json et laisser faire composer tout seul:

composer require symfony/http-foundation

qui créera de lui-même ce fichier composer.json tout en installant le composant demandé dans le dossier vendor du répertoire courant.

HttpFoundation:

Les 2 principaux composants de HttpFoundation à savoir Request et Response sont alors prêts à l’emploi.

Remarquez l’usage des espaces de nommages en PHP semblables à ceux du C++ ou aux import de packages en java pour éviter des conflits de nommages entre différents vendor c’est à dire différentes entités fournissant du code à votre projet. L”autoloader permet le chargement dynamique des classes concernées lors de leur utilisation.

<?php
  // chargement autoloader
  require_once __DIR__.'/vendor/autoload.php';

  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;

  // Actual request :
  //$request = Request::createFromGlobals();
  // fake request
  $request = Request::create('/essai.php?name=Zozo');

  // URI demandee (sans les parametres)
  $path=$request->getPathInfo();

 // recup de variables en GET
  $nom = $request->query->get('name','World');
  $prenom = $request->query->get('surname','Joe');
  echo "Bonjour $prenom $nom<br/>";

On peut aussi récupérer d’autres informations sur le Client et fabriquer une réponse:

<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

// toujours avec
$request = Request::create('/essai.php?name=Zozo');

// recup  variables SERVER
$host=$request->server->get('HTTP_HOST');

// get  COOKIES
$request->cookies->get('PHPSESSID');

// HTTP headers
$headers = $request->headers->get('host');
$content_type = $request->headers->get('content_type');

 // URI demandee (sans les parametres)
 $path = $request->getPathInfo();

$method = $request->getMethod();  //GET, POST, PUT, DELETE etc.
$langs = $request->getLanguages();
$IP = $request->getClientIp();
$response =
new Response($IP." ".$host." ".$path." ".$headers."
".$content_type." ".$method." ".$langs[0]);
$response->send();

Créer une application Symfony

Il existe 2 façons de procéder :

  • en utilisant l’installeur Symfony

  • en utilisant composer

voir par exemple la doc de symfony ou cette question stackoverflow

L’installeur symfony

Pour créer une application web traditionnelle :

symfony new --full my_project

Pour créer une app plus légère comme un microservice, une app console ou une API :

symfony new my_project

Pour installer la version LTS courante :

symfony new my_project --version=lts

Pour installer la dernière version stable :

symfony new my_project --version=stable

Pour installer la version de développement :

symfony new my_project --version=next

et pour installer une version spécifique :

symfony new my_project --version=4.4

Avec composer :

Pour créer une application web traditionnelle (ici en version 4.4) :

composer create-project symfony/website-skeleton my_project ^4.4.0

Pour créer une app plus légère comme un microservice, une app console ou une API (version 4.4 ici):

composer create-project symfony/skeleton my_project ^4.4.0

Squellette d’application Symfony :

Créons un répertoire de travail mvc-sf dans votre dossier Web, par exemple www ou ~/www (ou ~/public_html) si on utilise user_dir d’Apache ou plus simplement n’importe où si on utilise le serveur Web embarqué de PHP ou celui fourni par Symfony.

et créons la trame d’une application Symfony (sf) de type microservice à l’aide de symfony :

symfony new hello-sf

ou à l’aide de composer :

composer create-project symfony/skeleton hello-sf

Qui est beaucoup plus verbeux et vous montre qu’il y a tout de même pas mal de packages installés au passage :

🎵 💨
Installing symfony/skeleton (v5.0.99)
- Installing symfony/skeleton (v5.0.99): Loading from cache
Created project in hello-sf
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing symfony/flex (v1.6.2): Loading from cache
Symfony operations: 1 recipe (f2f9ab59a41987856b579e7321f4971e)
- Configuring symfony/flex (>=1.0): From github.com/symfony/recipes:master
Loading composer repositories with package information
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "5.0.*"
Package operations: 27 installs, 0 updates, 0 removals
- Installing psr/container (1.0.0): Loading from cache
- Installing symfony/service-contracts (v2.0.1): Loading from cache
- Installing symfony/polyfill-php73 (v1.14.0): Loading from cache
- Installing symfony/polyfill-mbstring (v1.14.0): Loading from cache
- Installing symfony/console (v5.0.5): Loading from cache
- Installing symfony/dotenv (v5.0.5): Loading from cache
- Installing symfony/routing (v5.0.5): Loading from cache
- Installing symfony/finder (v5.0.5): Loading from cache
- Installing symfony/filesystem (v5.0.5): Loading from cache
- Installing psr/log (1.1.3): Loading from cache
- Installing symfony/polyfill-intl-idn (v1.14.0): Loading from cache
- Installing symfony/mime (v5.0.5): Loading from cache
- Installing symfony/http-foundation (v5.0.5): Loading from cache
- Installing psr/event-dispatcher (1.0.0): Loading from cache
- Installing symfony/event-dispatcher-contracts (v2.0.1): Loading from cache
- Installing symfony/event-dispatcher (v5.0.5): Loading from cache
- Installing symfony/var-dumper (v5.0.5): Loading from cache
- Installing symfony/error-handler (v5.0.5): Loading from cache
- Installing symfony/http-kernel (v5.0.5): Loading from cache
- Installing symfony/dependency-injection (v5.0.5): Loading from cache
- Installing symfony/config (v5.0.5): Loading from cache
- Installing symfony/var-exporter (v5.0.5): Loading from cache
- Installing psr/cache (1.0.1): Loading from cache
- Installing symfony/cache-contracts (v2.0.1): Loading from cache
- Installing symfony/cache (v5.0.5): Loading from cache
- Installing symfony/framework-bundle (v5.0.5): Loading from cache
- Installing symfony/yaml (v5.0.5): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 3 recipes (f2f9ab59a41987856b579e7321f4971e)
- Configuring symfony/framework-bundle (>=4.4): From github.com/symfony/recipes:master
- Configuring symfony/console (>=4.4): From github.com/symfony/recipes:master
- Configuring symfony/routing (>=4.2): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install public [OK]

On peut aussi créer une application Web plus complète (soyez un peu plus patients dans ce cas …) avec :

composer create-project symfony/website-skeleton sf-full-project

ou

symfony new --full sf-full-project

Qui installera beaucoup plus de paquets, y compris l’ORM doctrine.

Veillez à bien avoir une version à jour de composer et si besoin:

composer self-update

La structure du projet skeleton hello-sf ainsi créé est la suivante :

hello-sf/
├── bin
│   └── console
├── composer.json
├── composer.lock
├── config
├── ├── boostrap.php
│   ├── bundles.php
│   ├── packages
│   ├── routes
│   ├── routes.yaml
│   └── services.yaml
├── public
│   └── index.php
├── src
│   ├── Controller
│   └── Kernel.php
├── symfony.lock
├── var
│   ├── cache
│   └── log
└── vendor
    ├── autoload.php
    ├── bin
    ├── composer
    ├── psr
    └── symfony

Le répertoire bin contient l’outil console qui permet d’effectuer les tâches de routine pour créer ou gérer un projet. Le répertoire config contient les fichiers de configuration. Le répertoire public contient le fichier index de l’application Le dossie src les controleurs, le Kernel mais aussi les entités etc. Le dossier var contient les cache et les logs et le dossier vendor les classes des Bundles installés comme http-foundation.

Vous pouvez consulter le fichier symfony.lock qui se trouve à la racine du dossier hello-sf pour voir la liste complète des dépendances installées:

{
    "php": {
        "version": "7.3"
    },
    "psr/cache": {
        "version": "1.0.1"
    },
    "psr/container": {
        "version": "1.0.0"
    },
    "psr/event-dispatcher": {
        "version": "1.0.0"
    },
    "psr/log": {
        "version": "1.1.3"
    },
    "symfony/cache": {
        "version": "v5.0.5"
    },
    "symfony/cache-contracts": {
        "version": "v2.0.1"
    },
    "symfony/config": {
        "version": "v5.0.5"
    },
    "symfony/console": {
        "version": "4.4",
        "recipe": {
            "repo": "github.com/symfony/recipes",
            "branch": "master",
            "version": "4.4",
            "ref": "ea8c0eda34fda57e7d5cd8cbd889e2a387e3472c"
        },
        "files": [
            "bin/console",
            "config/bootstrap.php"
        ]
    },
    "symfony/dependency-injection": {
        "version": "v5.0.5"
    },
    "symfony/dotenv": {
        "version": "v5.0.5"
    },
    "symfony/error-handler": {
        "version": "v5.0.5"
    },
    "symfony/event-dispatcher": {
        "version": "v5.0.5"
    },
    "symfony/event-dispatcher-contracts": {
        "version": "v2.0.1"
    },
    "symfony/filesystem": {
        "version": "v5.0.5"
    },
    "symfony/finder": {
        "version": "v5.0.5"
    },
    "symfony/flex": {
        "version": "1.0",
        "recipe": {
            "repo": "github.com/symfony/recipes",
            "branch": "master",
            "version": "1.0",
            "ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
        },
        "files": [
            ".env"
        ]
    },
    "symfony/framework-bundle": {
        "version": "4.4",
        "recipe": {
            "repo": "github.com/symfony/recipes",
            "branch": "master",
            "version": "4.4",
            "ref": "23ecaccc551fe2f74baf613811ae529eb07762fa"
        },
        "files": [
            "config/bootstrap.php",
            "config/packages/cache.yaml",
            "config/packages/framework.yaml",
            "config/packages/test/framework.yaml",
            "config/routes/dev/framework.yaml",
            "config/services.yaml",
            "public/index.php",
            "src/Controller/.gitignore",
            "src/Kernel.php"
        ]
    },
    "symfony/http-foundation": {
        "version": "v5.0.5"
    },
    "symfony/http-kernel": {
        "version": "v5.0.5"
    },
    "symfony/mime": {
        "version": "v5.0.5"
    },
    "symfony/polyfill-intl-idn": {
        "version": "v1.14.0"
    },
    "symfony/polyfill-mbstring": {
        "version": "v1.14.0"
    },
    "symfony/polyfill-php73": {
        "version": "v1.14.0"
    },
    "symfony/routing": {
        "version": "4.2",
        "recipe": {
            "repo": "github.com/symfony/recipes",
            "branch": "master",
            "version": "4.2",
            "ref": "683dcb08707ba8d41b7e34adb0344bfd68d248a7"
        },
        "files": [
            "config/packages/prod/routing.yaml",
            "config/packages/routing.yaml",
            "config/routes.yaml"
        ]
    },
    "symfony/service-contracts": {
        "version": "v2.0.1"
    },
    "symfony/var-dumper": {
        "version": "v5.0.5"
    },
    "symfony/var-exporter": {
        "version": "v5.0.5"
    },
    "symfony/yaml": {
        "version": "v5.0.5"
    }
}

Squellette d’application web Symfony

Si on crée une application complète avec :

symfony new --full my_project

On obtient une sctructure plus complète :

my_project
|-- bin
|   |-- console
|   `-- phpunit
|-- composer.json
|-- composer.lock
|-- config
|   |-- bootstrap.php
|   |-- bundles.php
|   |-- packages
|   |-- routes
|   |-- routes.yaml
|   `-- services.yaml
|-- phpunit.xml.dist
|-- public
|   `-- index.php
|-- src
|   |-- Controller
|   |-- Entity
|   |-- Kernel.php
|   |-- Migrations
|   `-- Repository
|-- symfony.lock
|-- templates
|   `-- base.html.twig
|-- tests
|   `-- bootstrap.php
|-- translations
|-- var
|   |-- cache
|   `-- log
`-- vendor
    |-- autoload.php
    |-- bin
    |-- composer
    |-- doctrine
    |-- easycorp
    |-- egulias
    |-- jdorn
    |-- monolog
    |-- nikic
    |-- ocramius
    |-- phpdocumentor
    |-- psr
    |-- sensio
    |-- symfony
    |-- twig
    |-- webmozart
    `-- zendframework

Nous allons ensuite compléter cette application Symfony par un CRUD puis une API.

Table des matières

Sujet précédent

Tester une API

Sujet suivant

Débuts avec Symfony