Upload
hugo-hamon
View
20.708
Download
7
Embed Size (px)
DESCRIPTION
Paradoxalement, Symfony2 n'est pas qu'un framework "full-stack". Il s'agit avant tout d'une parfaite synergie de briques logicielles autonomes qui travaillent de concert sous la baguette d'un seul chef d'orchestre : le conteneur d'injection de dépendances. Mais savez-vous que vous pouvez aussi les utiliser sans le framework ? Tous ces composants indépendants sont distribués sous licence MIT et offrent aux développeurs la liberté de les utiliser dans leurs projets PHP. Au cours de cette session, nous mettrons en lumière les fonctionnalités offertes par les principaux composants de Symfony2 tels que DependencyInjection, Console, Finder, EventDispatcher, Translation et bien d'autres encore. Vous découvrirez comment les intégrer et les utiliser dans vos projets PHP, et ainsi devenir le prochain Maestro du web.
Citation preview
Symfony2 en pièces détachées
Symfony Live 2011 - 4 mars 2011 - Paris
Hugo Hamon aka @hhamon
² Responsable des formations Sensio Labs
² Ancien membre du Bureau de l’AFUP ² Auteur d’ouvrage PHP / Symfony chez Eyrolles
² http://www.hugohamon.com
Avant d’oublier…
http://joind.in/talk/view/2766
Quelques questions…
Qui développe encore des applications sans framework ou bibliothèques ?
Quelques questions…
Qui réutilise des composants éprouvés ? Zend Framework, PEAR, EZ Components…
Quelques questions…
Qui réinvente à la roue ?
Symfony2 est bâti autour d’une bibliothèque de composants indépendants
Les composants
§ BrowserKit § ClassLoader § Console § CSS Selector § Dependency Injection § Dom Crawler § Event Dispatcher § Finder § Form § HTTP Foundation
§ HTTP Kernel § Locale § Process § Routing § Security § Serializer § Templating § Translation § Validator § YAML
ClassLoader
ClassLoader
ClassLoader
UniversalClassLoader
ClassLoader UniversalClassLoader
use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Symfony' => __DIR__ .'/symfony/src', 'Doctrine' => __DIR__ .'/doctrine/lib', 'Zend' => __DIR__ .'/zend/library', )); $loader->register();
PHP 5.3 Interoperability
standard
ClassLoader UniversalClassLoader
use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Sensio' => array(__DIR__ .'/src', __DIR__.'/sensio-extra') 'Symfony' => __DIR__ .'/symfony/src', 'Doctrine' => __DIR__ .'/doctrine/lib', 'Zend' => __DIR__ .'/zend/library', )); $loader->register(); Fallback
ClassLoader UniversalClassLoader
use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerPrefixes(array( 'Swift_' => __DIR__ .'/swiftmailer/lib/classes', 'Twig_' => __DIR__ .'/twig/lib/classes', )); $loader->register();
PEAR style
HttpFoundation
HttpFoundation
HttpFoundation
Request
HttpFoundation
Classe Dé!nition
Request Gestion des entêtes et paramètres GET, POST, Cookie, Server…
Response Gestion des entêtes et contenu de la réponse
Session Gestion de la session PHP
Cookie Génère un nouveau cookie
File Gestion des !chiers
HttpFoundation Request
require_once __DIR__ .'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $name = $request->query->get('name'); // Get query string variable $name = $request->request->get('name'); // Get post variable $name = $request->cookies->get('name'); // Get cookie value $file = $request->files->get('avatar'); // Get uploaded file $method = $request->getMethod(); // Get the request method $ip = $request->getClientIp(); // Get remote address $https = $request->isSecure(); // True if HTTPS $ajax = $request->isXmlHttpRequest(); // True if ajax request
HttpFoundation
Session
HttpFoundation Session
§ Lecture et écriture de variables de session § Lecture et écriture de messages "ash § Invalidation et nettoyage de la session § Choix du moteur de stockage
§ ArraySessionStorage : session non persistente § NativeSessionStorage : session PHP native § PdoSessionStorage : session en base de données
HttpFoundation Session native de PHP
use Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage; $session = new Session(new NativeSessionStorage()); $session->start(); // Read the session id $id = $session->getId(); // Set a session variable $session->set('username', 'hhamon'); // Set a flash message $session->setFlash('notice', 'You win!');
HttpFoundation Session native de PHP
use Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage; $session = new Session(new NativeSessionStorage()); $session->start(); // Read the session variable echo $session->get('username'); // Read a flash message if ($session->hasFlash('notice')) { echo $session->getFlash('notice'); } // Remove a session variable $session->remove('username'); $session->clear();
HttpFoundation Session en base de données
§ Utilisation des objets PdoSessionStorage et PDO
require_once __DIR__ .'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\SessionStorage\PdoSessionStorage; $pdo = new \PDO('mysql:host=localhost;dbname=sflive2011', 'root', '', array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION )); $storage = new PdoSessionStorage($pdo, array('db_table' => 'php_session')); $session = new Session($storage, array('default_locale' => 'fr')); $session->start(); $session->set('username', 'hhamon');
HttpFoundation Session en base de données
§ Création de la table dans la base de données
§ Contrôle des données de session dans la table
CREATE TABLE `php_session` ( `sess_id` varchar(40) COLLATE utf8_unicode_ci NOT NULL, `sess_data` text COLLATE utf8_unicode_ci, `sess_time` INTEGER(10) UNSIGNED NOT NULL, PRIMARY KEY (`sess_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
HttpFoundation
Response
HttpFoundation Response § Support des entêtes HTTP
§ Content-Type § Status-Code
§ Support du cache HTTP § Expires § Etag § Cache-Control (max-age, s-maxage…)
§ Support des cookies
HttpFoundation Response - Headers
HTTP/1.0 200 OK cache-control: s-maxage=3600 content-type: text/html; charset=utf-8 etag: "article-1337" <html>...</html>
use Symfony\Component\HttpFoundation\Response; $response = Response('<html>...</html>', 200); $response->headers->set('Content-Type', 'text/html; charset=utf-8'); $response->setEtag('article-1337'); $response->setSharedMaxAge(3600); $response->send();
HttpFoundation
Cookie
HttpFoundation Response - Cookie
HTTP/1.0 200 OK content-type: text/html; charset=utf-8 set-cookie: last-viewed=1337; expires=Mon, 10-Jan-2011 00:52:24 GMT; path=/; httponly <html>...</html>
require_once __DIR__ .'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Cookie; $cookie = new Cookie('last-viewed', '1337', time()+3600*24); $response = new Response('<html>...</html>', 200); $response->headers->setCookie($cookie); $response->send();
HttpFoundation
File
HttpFoundation Fichiers et uploads
use Symfony\Component\HttpFoundation\File\File; // Set the document root File::setDocumentRoot(__DIR__); $file = new File(__DIR__.'/../tmp/avatar.png'); // Change the file location & rename it on the fly $file->move(__DIR__.'/images', 'hhamon.png'); $ext = $file->getExtension(); // Get the file extension $size = $file->getMimeType(); // Get the file type $path = $file->getPath(); // Get the file path echo '<img src="', $file->getWebPath() ,'" alt="" />';
HttpFoundation Fichier et uploads
§ Uploader un !chier avec Request et UploadedFile
<form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="avatar"/> <button type="submit">upload</button> </form>
require __DIR__ .'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\File\UploadedFile; $request = Request::createFromGlobals(); $avatar = $request->files->get('avatar'); if ($avatar instanceOf UploadedFile && UPLOAD_ERR_OK == $avatar->getError()) { $avatar->move(__DIR__.'/images'); }
Event Dispatcher
Event Dispatcher
Event Dispatcher Introduction
§ Implémentation du pattern « Observateur »
§ 2 classes et 2 interfaces
§ Faciliter le découplage entre les classes
§ Améliorer la "exibilité / réutilisabilité du code
§ Simpli!er l’intégration de plugins tiers
Event Dispatcher La classe EventDispatcher
§ Connecte des écouteurs (« callables ») à des événements
§ Les sujets « noti!ent » des événements aux écouteurs
$dispatcher = new EventDispatcher(); $dispatcher->connect('the.event.name', $callable);
class Subject { public $dispatcher; public function doThings() { $event = new Event($this, 'the.event.name'); $this->dispatcher->notify($event); } }
Event Dispatcher
« Forcer le nettoyage d’un cache lorsque certains événements se
produisent »
Event Dispatcher Vider un cache
class WikiPage { public $id; public $title; public $content; public function save(\PDO $conn) { if (!$this->id) { $conn->query('INSERT INTO ...'); } else { $conn->exec('UPDATE ...'); } } }
En cas de modi!cation, la page html statique du cache doit être régénérée…
namespace My\Domain; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; class WikiPage { // ... private $dispatcher; public function __construct(EventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function save(\PDO $conn) { if (!$this->id) { // ... } else { $conn->exec('UPDATE ...'); $args = array('type' => 'wiki', 'id' => $this->id); $event = new Event($this, 'cache.flush', $args); $this->dispatcher->notify($event); } } }
Un événement est noti!é pour demander aux écouteurs de vider le cache de l’objet.
namespace My\Cache; use Symfony\Component\EventDispatcher\Event; class CacheManager { private $cacheDir; public function __construct($cacheDir) { $this->cacheDir = $cacheDir; } public function listenToCacheFlushEvent(Event $event) { // The WikiPage object $object = $event->getSubject(); // Retrieve all extra arguments $args = $event->all(); $path = $this->cacheDir .'/'. $args['type'] .'/'. $args['id'] .'.html'; if (file_exists($path)) { @unlink($path); } } }
Ecouteur invoqué à la noti!cation de l’événement.
use Symfony\Component\EventDispatcher\EventDispatcher; use My\Domain\WikiPage; use My\Cache\CacheManager; $pdo = new \PDO('...'); $cache = new CacheManager('/path/to/cache'); # Register a new listener. $dispatcher = new EventDispatcher(); $dispatcher->connect('cache.flush', array($cache, 'listenToCacheFlushEvent')); # Find the wiki page object with its id. $page = WikiPage::findById($_GET['id']); $page->setEventDispatcher($dispatcher); # Page properties are edited. $page->title = 'New title'; $page->content = 'New content'; # The CacheManager::listenToCacheFlushEvent method is called. $page->save($pdo);
Event Dispatcher
« Filtrer / transformer une valeur »
Event Dispatcher Filtrer une donnée
class WikiPage { private $rawContent; private $parsedContent; public function setRawContent($rawContent) { $this->rawContent = $rawContent; $parser = new WikiParser(); $this->parsedContent = $parser->parse($rawContent); } }
Event Dispatcher Problème ?
§ Les objets « WikiPage » et « WikiParser » sont fortement couplés
§ Changer le parser implique de modi!er la classe « WikiPage »
§ Idem, si l’on veut ajouter des !ltres supplémentaires
§ Testabilité du code réduite due à la dépendance
Event Dispatcher Solution?
class WikiPage { private $rawContent; private $parsedContent; public function setRawContent($rawContent) { $$this->rawContent = $rawContent; $event = new Event('wiki.filter_content', $rawContent); $this->parsedContent = $this->dispatcher->filter($event); } }
§ Filtrer la valeur en appelant une série de !ltres (« écouteurs »).
use Symfony\Component\EventDispatcher\EventDispatcher; use My\Domain\WikiPage; use My\Filter\WikiSyntaxParser; $dispatcher = new EventDispatcher(); # Register a first filter $dispatcher->connect('wiki.filter_content', function(Event $e, $value) { return striptags($value, '<code>') }); $wikiParser = new WikiSyntaxParser(); # Register a second filter $dispatcher->connect('wiki.filter_content', array($wikiParser, 'parse')); $wikiPage = new WikiPage(); $wikiPage->setEventDispatcher($dispatcher); # Set and filter the raw content value $wiki->setRawContent('Some **wiki** content with <script>alert("foo")</script> <code>$c = $a + $b</code>.');
Event Dispatcher Filtrer une valeur
§ La propriété « parsedContent » aura la valeur suivante.
Some <strong>wiki</strong> content with <code>$c = $a + $b</code>.
§ La balise <script> a été !ltrée
§ La balise <code> a été conservée
§ La syntaxe Wiki a été transformée en HTML
Dependency Injection
Dependency Injection
Dependency Injection Introduction
§ « Inversion de contrôle »
§ Composant au cœur de Symfony2
§ Instanciation et initialisation de services à la demande
§ Supporte les dépendances entre les différents services
§ Dé!nition des services en PHP, XML ou YAML
Dependency Injection Dé!nition d’un service SOAP
# config/services.xml <?xml version="1.0" ?> <container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd"> <parameters> <parameter key="soap_server.wsdl.uri"> http://www.components.local/soap-server.php?wsdl </parameter> <parameter key="soap_server.response.return">true</parameter> <parameter key="soap_server.options.encoding">utf-8</parameter> <parameter key="calculator_service.class">Application\Calculator</parameter> </parameters> <services> <!-- ... --> </services> </container>
Paramètres de configura1on des services.
Dependency Injection Dé!nition d’un service SOAP
<?xml version="1.0" ?> <container ... > <!-- ... --> <services> <!-- SOAP Server --> <service id="soap_server" class="Zend\Soap\Server"> <argument>%soap_server.wsdl.uri%</argument> <argument type="collection"> <argument key="encoding">%soap_server.options.encoding%</argument> </argument> <call method="setReturnResponse"> <argument>%soap_server.response.return%</argument> </call> <call method="setObject"><argument type="service" id="calculator_service" /></call> </service> <!-- SOAP Autodiscovery --> <service id="soap_autodiscover" class="Zend\Soap\Autodiscover"> <call method="setClass"><argument type="string">%calculator_service.class%</argument></call> </service> <!-- Calculator service used by the SOAP server --> <service id="calculator_service" class="%calculator_service.class%"/> </services> </container>
Défini1on d’un serveur Zend Soap comme étant un service
Dependency Injection Dé!nition d’un service SOAP
<!-- Identification du service : identifiant + type --> <service id="soap_server" class="Zend\Soap\Server"> <!-- 1er argument du constructeur : valeur d’un paramètre défini précédemment --> <argument>%soap_server.wsdl.uri%</argument> <!-- 2nd argument du constructeur : tableau associatif d’options --> <argument type="collection"> <argument key="encoding">utf-8</argument> </argument> <!– 1ère méthode à appeler juste après l’instanciation du service --> <call method="setReturnResponse"> <argument>true</argument> </call> <!– 2nd méthode à appeler avec une instance du service calculator_service en argument --> <call method="setObject"> <argument type="service" id="calculator_service" /> </call> </service>
Dependency Injection Dé!nition du service Calculator
namespace Application; class Calculator { /** * Returns the sum of two numbers. * * @param float $a The first operand * @param float $b The second operand * @return float The result * @throws SoapFault */ public function sum($a, $b) { if (!is_numeric($a) || !is_numeric($b)) { throw new SoapFault('The sum() operation only accepts numeric arguments.'); } return $a + $b; } }
Documenta1on avec de la PHPDoc afin de générer le WSDL correspondant grâce à
Zend_Soap_Autodiscover
Dependency Injection Obtenir le conteneur de services
# web/soap-server.php use Symfony\Component\Config\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
$container = new ContainerBuilder(new ParameterBag());
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$loader->load('services.xml');
if (isset($_GET['wsdl'])) {
$autodiscover = $container->get('soap_autodiscover');
$response = $autodiscover->toXml();
} else {
$soapServer = $container->get('soap_server');
$response = $soapServer->handle();
}
header('Content-Type: text/xml');
echo $response;
Lecture du fichier services.xml et chargement de la défini1on dans le conteneur d’injec1on de dépendances
Récupéra1on du serveur SOAP Zend correctement instancié et ini1alisé.
Finder
Finder
Finder Rechercher des !chiers
§ Rechercher des !chiers dans une arborescence § Filtres sur des critères
§ Type (!chiers ou répertoires) § Nom et extension (patterns) § Taille § Date § …
§ Tri les résultats sur des attributs de !chiers
Finder Rechercher des !chiers
use Symfony\Component\Finder\Finder;
$finder = new Finder();
// All files in the current folder
$files = $finder->files()->in(__DIR__);
// All directories in the current folder
$files = $finder->directories()->in(__DIR__);
// All PHP files in the current folder only
$files = $finder->files()->name('/\.php$/')->depth(0)->in(__DIR__);
// All files whose size is between 1K and 2K inclusive
$files = $finder->files()->size('>= 1K')->size('<= 2K')->in(__DIR__);
Finder Rechercher des !chiers
// Search files in several folders
$files = $finder->files()->in(array(__DIR__, __DIR__.'/../src'));
// Sort file by name
$files = $finder->files()->name('/\.php$/')->in(__DIR__)->sortByName();
// Sort files by type
$files = $finder->files()->name('/\.php$/')->in(__DIR__)->sortByType();
// Filter by date
$files = $finder->files()->name('/\.(png|jpg|jpeg|gif)$/i')->
date('>= 2011-01-09')->
date('<= 2011-02-03')->in(__DIR__);
$files = $finder->files()->name('/\.(png|jpg|jpeg|gif)$/i')->
date('since 2011-01-09')->
date('before 2011-02-03')->in(__DIR__);
Finder Filtres personnalisés
# Create a lambda function that acts as a filter.
$onlyWritableFiles = function(\SplFileInfo $file) {
return $file->isWritable();
};
Un !ltre personnalisé est une fonction anonyme qui reçoit une instance de \SplFileInfo et retourne une valeur booléenne. # Find writeable TXT files
$finder = new Finder(); $files = $finder->files()-> name('/\.txt$/')-> filter($onlyWritableFiles)-> in(__DIR__.'/../cache');
foreach (iterator_to_array($files) as $path => $file) { echo $file->getFilename(); }
Templating
Templating
Templating Introduction § Système de templating simple et efficace § Supporte des moteurs de stockage différents
§ Fichiers § Chaînes de caractères
§ Supporte des fonctionnalités « modernes » § Héritage de templates § Slots § Aide de vue (« helpers »)
§ Extensible
Templating Génération d’un template simple
§ Chargement et rendu d’un template simple
Mo1fs de chemins de fichiers de templates
Variables du template
require_once __DIR__ .'/../src/autoload.php'; use Symfony\Component\Templating\PhpEngine; use Symfony\Component\Templating\TemplateNameParser; use Symfony\Component\Templating\Loader\FilesystemLoader; $loader = new FilesystemLoader(array( __DIR__.'/../src/tpl/%name%.php', __DIR__.'/../src/tpl/hello/%name%.php', )); $engine = new PhpEngine(new TemplateNameParser(), $loader); echo $engine->render('index', array('name' => 'Hugo'));
Templating Génération d’un template simple
§ La variable $view correspond à l’objet $engine précédent.
§ L’échappement des variables est assuré par la méthode escape() de l’objet $view.
# src/tpl/index.php <p> Hello <?php echo $view->escape($name) ?>! </p>
use Symfony\Component\Templating\PhpEngine; use Symfony\Component\Templating\TemplateNameParser; use Symfony\Component\Templating\Loader\FilesystemLoader; use Symfony\Component\Templating\Helper\SlotsHelper; $loader = new FilesystemLoader(array( __DIR__.'/../src/tpl/%name%.php', __DIR__.'/../src/tpl/hello/%name%.php', )); $engine = new PhpEngine(new TemplateNameParser(), $loader, array( new SlotsHelper() )); $content = $engine->render('index', array('name' => 'Hugo')); echo $content;
Templating Génération d’un template avancé
Support des aides de vue et de l’héritage de templates.
# src/tpl/layout.php <html> <head> <title> <?php $view['slots']->output('title', 'Templating component') ?> </title> </head> <body> <h1>Welcome!</h1> <?php $view['slots']->output('_content') ?> </body> </html>
# src/tpl/index.php <?php $view->extend('layout') ?> <?php $view['slots']->set('title', 'Hello Application') ?> <p> Hello <?php echo $view->escape($name) ?>! </p>
Templating Génération d’un template avancé
Extension du template de base
Dé!nition de la valeur d’un slot
Contenu évalué de index.php
Affichage du slot ou une valeur par défaut si non dé!ni
Templating Génération d’un template avancé
<!DOCTYPE html> <html> <head> <title>Hello Application</title> </head> <body> <h1>Welcome!</h1> <p> Hello Hugo! </p> </body> </html>
§ Le rendu !nal est le suivant :
Translation
Translation
Translation Introduction § Gestionnaire de dictionnaires de traduction § Supporte différents formats de stockage
§ Array § PHP § YAML § XLIFF § CSV
§ Supporte les chaînes dynamiques § Embarque des algorithmes de pluralisation
Translation Dé!nition d’un dictionnaire XLIFF
# i18n/fr/messages.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="messages.xml"> <header/> <body> <trans-unit id="1"> <source>Hello</source> <target>Salut</target> </trans-unit> <trans-unit id="2"> <source>Hello %name%</source> <target>Bonjour %name%</target> </trans-unit> </body> </file> </xliff>
require __DIR__ .'/../src/autoload.php'; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Loader\XliffFileLoader; // Création d’un objet Translator $translator = new Translator('fr', new MessageSelector()); // Chargement du dictionnaire XLIFF $translator->addLoader('xliff', new XliffFileLoader()); $translator->addResource('xliff', __DIR__.'/../i18n/fr/messages.xml', 'fr'); // Returns "Salut" echo $translator->trans('Hello'); // Returns "Bonjour Hugo" echo $translator->trans('Hello %name%', array('%name%' => 'Hugo'));
Translation Traduire des chaînes d’un catalogue
Translation Utiliser une locale par défaut
§ Dé!nition d’un second dictionnaire pour l’allemand # i18n/de/messages.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="messages.xml"> <header/> <body> <trans-unit id="1"> <source>Welcome in %city%</source> <target>Wilkommen in %city%</target> </trans-unit> </body> </file> </xliff>
Translation Utiliser une locale par défaut
§ Le gestionnaire de traduction peut charger plusieurs catalogues de traduction et forcer une locale par défaut.
$trans = new Translator('fr', new MessageSelector()); $trans->addLoader('xliff', new XliffFileLoader()); // Load two distinct dictionaries $trans->addResource('xliff', __DIR__.'/../i18n/de/messages.xml', 'de'); $trans->addResource('xliff', __DIR__.'/../i18n/fr/messages.xml', 'fr'); // Set the default fallback locale $trans->setFallbackLocale('de'); // Translation only exists in german catalog even if the locale is fr echo $trans->trans('Welcome in %city%', array('%city%' => 'Paris'));
Translation Pluralisation des chaînes
# i18n/fr/messages.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="messages.xml"> <header/> <body> <!-- ... --> <trans-unit id="3"> <source>{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples</source> <target>{0} Il n'y a pas de pomme|{1} Il y a une pomme|]1,Inf] Il y a %count% pommes</target> </trans-unit> </body> </file> </xliff>
Translation Pluralisation des chaînes
§ La méthode transChoice() facilite la pluralisation des chaînes $count = 5; $translator = new Translator('fr', new MessageSelector()); $translator->addLoader('xliff', new XliffFileLoader()); $translator->addResource('xliff', __DIR__.'/../i18n/fr/messages.xml', 'fr'); // Returns "Il y a <strong>5</strong> pommes" echo $translator->transChoice( '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', $count, array('%count%' => '<strong>' . $count . '</strong>') );
Locale
Locale
Locale Introduction
§ Surcharge la classe Locale de PHP 5.3
§ Supporte les formatages des noms de pays
§ Supporte les formatages des noms de langues
§ L’API se compose uniquement de méthodes statiques
Locale Obtenir la listes des pays
Array ( [AF] => Afghanistan [ZA] => Afrique du Sud [AL] => Albanie ... )
Array ( [AF] => Afghanistan [AL] => Albania [ZA] => South Africa ... )
Array ( [AF] => Afghanistan [AL] => Albanien [ZA] => Südafrika ... )
§ Les pays sont retournés dans des tableaux associatifs triés sur les valeurs
use Symfony\Component\Locale\Locale; print_r(Locale::getDisplayCountries('fr')); print_r(Locale::getDisplayCountries('en')); print_r(Locale::getDisplayCountries('de'));
Locale Codes ISO des pays
Array ( [0] => AF [1] => AX [2] => AL [3] => DZ [4] => AS [5] => AD [6] => AO [7] => AI [8] => AQ [9] => AG [10] => AR [11] => AM [12] => AW [13] => AC [14] => AU [15] => AT [16] => AZ [17] => BS [18] => BH [19] => BD [20] => BB [21] => BY [22] => BE [23] => BZ [24] => BJ [25] => BM [26] => BT [27] => BO [28] => BA [29] => BW [30] => BV [31] => BR [32] => IO [33] => VG [34] => BN [35] => BG [36] => BF [37] => BI [38] => KH [39] => CM [40] => CA [41] => IC [42] => CV [43] => KY [44] => CF [45] => EA [46] => TD [47] => CL [48] => CN [49] => CX [50] => CP [51] => CC [52] => CO [53] => KM [54] => CG [55] => CD [56] => CK [57] => CR [58] => CI [59] => HR [60] => CU [61] => CY [62] => CZ [63] => DK [64] => DG [65] => DJ [66] => DM [67] => DO [68] => EC [69] => EG [70] => SV [71] => GQ [72] => ER [73] => EE ...)
use Symfony\Component\Locale\Locale; print_r(Locale::getCountries());
Locale Obtenir la listes des locales
Array ( [fr_BE] => français (Belgique) [fr_CA] => français (Canada) [fr_DJ] => français (Djibouti) [fr_LU] => français (Luxembourg) [fr_CH] => français (Suisse) ... )
§ Les locales sont retournées dans des tableaux associatifs triés sur les valeurs
Array ( [fr_BE] => French (Belgium) [fr_CA] => French (Canada) [fr_DJ] => French (Djibouti) [fr_LU] => French (Luxembourg) [fr_CH] => French (Switzerland) ... )
use Symfony\Component\Locale\Locale; print_r(Locale::getDisplayLocales('fr')); print_r(Locale::getDisplayLocales('en'));
Locale Codes ISO des locales
Array ( [0] => af [1] => af_NA [2] => ak [3] => sq [4] => am [5] => am_ET [6] => ar [7] => ar_DZ [8] => ar_BH [9] => ar_EG [10] => ar_IQ [11] => ar_JO [12] => ar_KW [13] => ar_LB [14] => ar_LY [15] => ar_MA [16] => ar_OM [17] => ar_QA [18] => ar_SA [19] => ar_SD [20] => ar_SY [21] => ar_TN [22] => ar_AE [23] => ar_YE [24] => hy [25] => as [26] => as_IN [27] => asa [28] => az [29] => az_Cyrl [30] => az_Cyrl_AZ [31] => az_Latn [32] => az_Latn_AZ [33] => bm [34] => eu [35] => be [36] => bem [37] => bez [38] => bn [39] => bn_IN [40] => bs [41] => bg [42] => my [43] => my_MM [44] => ca [45] => tzm [46] => tzm_Latn [47] => tzm_Latn_MA [48] => chr [49] => chr_US [50] => cgg [51] => zh [52] => kw [53] => hr [54] => cs [55] => da [56] => nl [57] => nl_BE [58] => ebu [59] => ebu_KE [60] => en [61] => en_AS [62] => en_AU [63] => en_BE [64] => en_BZ [65] => en_BW [66] => en_CA [67] => en_GU [68] => en_HK [69] => en_IN ...)
use Symfony\Component\Locale\Locale; print_r(Locale::getLocales());
Locale Obtenir la listes des langues
Array ( [en] => anglais [en_US] => anglais américain [en_AU] => anglais australien [en_GB] => anglais britannique [en_CA] => anglais canadien ... )
§ Les langues sont retournées dans des tableaux associatifs triés sur les valeurs
Array ( [en] => English [en_AU] => Australian English [en_GB] => British English [en_CA] => Canadian English [en_US] => U.S. English ... )
use Symfony\Component\Locale\Locale; print_r(Locale::getDisplayLanguages('fr')); print_r(Locale::getDisplayLanguages('en'));
Locale Codes ISO des langues
Array ( [0] => ab [1] => ace [2] => ach [3] => ada [4] => ady [5] => aa [6] => afh [7] => af [8] => afa [9] => ain [10] => ak [11] => akk [12] => sq [13] => ale [14] => alg [15] => tut [16] => am [17] => egy [18] => grc [19] => anp [20] => apa [21] => ar [22] => an [23] => arc [24] => arp [25] => arn [26] => arw [27] => hy [28] => rup [29] => art [30] => as [31] => ast [32] => asa [33] => ath [34] => cch [35] => en_AU [36] => aus [37] => de_AT [38] => map [39] => av [40] => ae [41] => awa [42] => ay [43] => az [44] => ban [45] => bat [46] => bal [47] => bm [48] => bai [49] => bad [50] => bnt [51] => bas [52] => ba [53] => eu [54] => btk [55] => bej [56] => be [57] => bem [58] => bez [59] => bn [60] => ber [61] => bho [62] => bh [63] => bik [64] => bin [65] => bi [66] => byn [67] => zbl [68] => brx [69] => bs [70] => bra [71] => pt_BR [72] => br [73] => en_GB [74] => bug [75] => bg [76] => bua [77] => my [78] => cad [79] => en_CA [80] => fr_CA [81] => yue [82] => car [83] ...)
use Symfony\Component\Locale\Locale; print_r(Locale::getLanguages());
Console
Console
Console Introduction
§ Automatiser des tâches pénibles (génération de code…)
§ Exécuter des tâches qui prennent du temps
§ Béné!cier des ressources du serveur
§ Plani!er des tâches dans une crontab
Console Introduction
§ Arguments et options
§ Codes de sortie
§ Shell
§ Coloration de la sortie
§ Testabilité
§ Messages d’erreur
§ …
Console Amorcer le CLI
#!/usr/bin/php<?phprequire __DIR__ .'/src/autoload.php';use Symfony\Component\Console\Application;$application = new Application('Console', '1.0');$application->run();
§ Création d’un !chier « console.php » exécutable (chmod +x)
Console Amorcer le CLI
namespace Application\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Application\Service\GoogleWeather; class WeatherCommand extends Command { protected function configure() { $this-> addArgument('city', InputArgument::REQUIRED, 'The city')-> addOption('lang', null, InputOption::VALUE_REQUIRED, 'The lang', 'en')-> setName('google:weather')-> setDescription('Fetches weather information.')-> setHelp(<<<EOF The <info>google:weather</info> command fetches weather data for a given city: <info>google:weather paris --lang fr</info> EOF ); }}
Dé!nition des meta données de la commande : nom, manuel, arguments, options…
Console Dé!nir la logique de la commande
class WeatherCommand extends Command { # ... protected function execute(InputInterface $input, OutputInterface $output) { $city = $input->getArgument('city'); $lang = $input->getOption('lang'); $weather = new GoogleWeather($city, $lang); $output->writeln(sprintf('<info>City</info>: %s', $weather->getCity())); $output->writeln(sprintf('<info>Temperature</info>: %s', $weather->getTemperature())); $output->writeln(sprintf('<info>Latitude</info>: %s', $weather->getLatitude())); $output->writeln(sprintf('<info>Longitude</info>: %s', $weather->getLongitude())); $output->writeln(sprintf('<info>Condition</info>: %s', $weather->getCondition())); $output->writeln(sprintf('<info>Wind</info>: %s', $weather->getWindCondition())); } }
$input : arguments + options $output : sortie de la console
Console Enregistrer la commande
#!/usr/bin/php<?phprequire __DIR__ .'/src/autoload.php';use Symfony\Component\Console\Application;use Application\Command\WeatherCommand;$application = new Application('Console', '1.0');$application->add(new WeatherCommand());$application->run();
Console Consulter la liste des commandes
Console Obtenir de l’aide de la commande
./console help google:weather
Console Exécuter la commande
./console google:weather Berlin --lang fr
Process
Process
Process Introduction
§ Exécuter des lignes de commande depuis un script PHP
§ Récupérer la sortie générée par la console
§ Récupérer le statut de l’exécution de la console
Process Mise en oeuvre
use Symfony\Component\Process\Process; $process = new Process('ls -lah', __DIR__); $process->run(); if (!$process->isSuccessful()) { $output = $process->getErrorOutput(); } else { $output = $process->getOutput(); } echo $output;
Process Mise en oeuvre total 176 drwxr-xr-x 25 Hugo staff 850B Feb 23 22:53 . drwxr-xr-x 9 Hugo staff 306B Feb 13 19:39 .. -rw-r--r--@ 1 Hugo staff 607B Feb 19 12:48 crawler.php -rw-r--r--@ 1 Hugo staff 1.9K Feb 13 12:08 event-dispatcher.php -rw-r--r--@ 1 Hugo staff 914B Feb 13 13:43 event-dispatcher2.php -rw-r--r--@ 1 Hugo staff 678B Jan 31 21:55 file.php -rw-r--r--@ 1 Hugo staff 1.7K Feb 12 14:40 finder.php drwxrwxrwx 3 Hugo staff 102B Jan 31 21:55 images -rw-r--r--@ 1 Hugo staff 276B Jan 9 23:39 locale.php -rw-r--r--@ 1 Hugo staff 454B Feb 23 22:53 process.php -rw-r--r--@ 1 Hugo staff 746B Jan 31 22:26 read-request.php -rw-r--r--@ 1 Hugo staff 545B Jan 31 22:41 response.php -rw-r--r--@ 1 Hugo staff 1.3K Feb 9 23:58 routing.php -rw-r--r--@ 1 Hugo staff 718B Feb 1 23:38 serializer.php -rw-r--r--@ 1 Hugo staff 502B Jan 9 00:37 session1.php -rw-r--r--@ 1 Hugo staff 682B Jan 9 00:37 session2.php -rw-r--r--@ 1 Hugo staff 520B Jan 9 01:15 session3.php -rw-r--r--@ 1 Hugo staff 241B Feb 6 11:28 soap-client.php -rw-r--r--@ 1 Hugo staff 737B Feb 19 10:45 soap-server.php -rw-r--r--@ 1 Hugo staff 558B Feb 19 10:56 templating-advanced.php -rw-r--r--@ 1 Hugo staff 476B Feb 19 10:56 templating-basic.php
PhpProcess Exécuter un script PHP
use Symfony\Component\Process\PhpProcess; $process = new PhpProcess('<?php echo "Hello World!" ?>'); $process->run(); if (!$process->isSuccessful()) { $output = $process->getErrorOutput(); } else { $output = $process->getOutput(); } echo $output;
Serializer
Serializer
Serializer Introduction
Le composant « Serializer » convertit des objets PHP en
représentations XML ou JSON par introspection.
Serializer Architecture
Serializer Architecture
§ L’objet « Serializer » transforme un objet PHP en représentation XML (ou JSON) et inversement.
§ Les objets « Normalizer » transforment un objet en tableau
PHP. § Les objets « Encoder » transforment un tableau PHP en
représentations XML ou JSON.
class Author { private $firstName; private $lastName; public function __construct($firstName, $lastName) { $this->firstName = $firstName; $this->lastName = $lastName; } public function getFirstName() { return $this->firstName; } public function getLastName() { return $this->lastName; } public function getFullName() { return $this->firstName .' '. $this->lastName; } }
class Book { private $title; private $author; public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function setAuthor(Author $author) { $this->author = $author; } public function getAuthor() { return $this->author; } }
Serializer Sérialiser un objet en XML
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; $serializer = new Serializer(); # Register the XML encoder $serializer->setEncoder('xml', new XmlEncoder()); # Register a getter / setter normalizer $serializer->addNormalizer(new GetSetMethodNormalizer()); $book = new Book(); $book->setAuthor(new Author('Agatha', 'Christie')); $book->setTitle('Ten Little Niggers'); header('Content-Type: text/xml; charset=utf-8'); echo $serializer->serialize($book, 'xml');
Serializer Sérialiser un objet en XML
Serializer Sérialiser un objet en JSON
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; $serializer = new Serializer(); # Register the JSON encoder $serializer->setEncoder('json', new JsonEncoder()); # Register a getter / setter normalizer $serializer->addNormalizer(new GetSetMethodNormalizer()); $book = new Book(); $book->setAuthor(new Author('Agatha', 'Christie')); $book->setTitle('Ten Little Niggers'); header('Content-Type: application/json; charset=utf-8'); echo $serializer->serialize($book, 'json');
Serializer Sérialiser un objet en JSON
{ "title":"Ten Little Niggers", "author": { "firstname":"Agatha", "lastname":"Christie", "fullname":"Agatha Christie" } }
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; $serializer = new Serializer(); $serializer->setEncoder('json', new JsonEncoder()); $serializer->addNormalizer(new GetSetMethodNormalizer()); $jsonEncoded = '{ "title":"Ten Little Niggers", "author": { "firstname":"Agatha", "lastname":"Christie", "fullname":"Agatha Christie" } }'; header('Content-Type: text/plain; charset=utf-8'); print_r($serializer->decode($jsonEncoded, 'json'));
Serializer Désérialiser un objet JSON
Array ( [title] => Ten Little Niggers [author] => Array ( [firstname] => Agatha [lastname] => Christie [fullname] => Agatha Christie ) )
YAML
YAML
YAML Introduction
YAML signi!e « Ain't Markup Language ». C’est un format
standard de sérialisation de données pour tous les
langages de programmation.
YAML Parser un !chier YAML
parameters: # Twitter module twitter.timeline.cache: true twitter.auth.username: hhamon twitter.auth.password: s3cr3t # Blog module blog.post.max_per_page: 10 app: available_cultures: [fr, en, de] services: db_connection: class: \PDO dsn: mysq:host=localhost;dbname=foo username: root password: ~
YAML Parser un !chier YAML
use Symfony\Component\Yaml\Parser;$content = file_get_contents(__DIR__ . '/../config/config.yml');$yaml = new Parser();$result = $yaml->parse($content);echo '<pre>’;print_r($result);echo '</pre>';
§ La classe « Parser » transforme une chaîne YAML en un tableau associatif PHP.
Array ( [parameters] => Array ( [twitter.timeline.cache] => 1 [twitter.auth.username] => hhamon [twitter.auth.password] => s3cr3t [blog.post.max_per_page] => 10 ) [app] => Array ( [available_cultures] => Array ( [0] => fr [1] => en [2] => de ) ) [services] => Array ( [db_connection] => Array ( [class] => \PDO [dsn] => mysq:host=localhost;dbname=foo [username] => root [password] => ) ) )
YAML Transformer un tableau en YAML
use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Dumper; $yaml = new Parser(); $result = $yaml->parse(file_get_contents(__DIR__ . '/../config/config.yml')); $result['parameters']['twitter.timeline.cache'] = false; $result['services']['db_connection']['class'] = '\PdoMock'; $dumper = new Dumper(); $data = $dumper->dump($result, 2); file_put_contents(__DIR__ . '/../config/config_test.yml', $data);
§ La classe « Dumper » transforme un tableau PHP en YAML
YAML Transformer un tableau en YAML
parameters: twitter.timeline.cache: false twitter.auth.username: hhamon twitter.auth.password: s3cr3t blog.post.max_per_page: 10 app: available_cultures: [fr, en, de] services: db_connection: { class: \PdoMock, dsn: 'mysq:host=localhost;dbname=foo', username: root, password: null }
CSS Selector
CSS Selector
CSS Selector CSS3 vers XPath
use Symfony\Component\CssSelector\Parser; $doc = new \DOMDocument(); $doc->loadHTMLFile(__DIR__.'/../src/tpl/page.html'); $xpath = new \DOMXPath($doc); $expr = Parser::cssToXpath('ul#menu > li a:contains("Home")'); $xpath->query($expr); foreach ($xpath->query($expr) as $link) { printf('%s > %s', $link->nodeValue, $link->getAttribute('href')); }
CSS Selector CSS3 vers XPath
Parser::cssToXpath('ul#menu > li a:contains("Home")');
descendant-or-self::ul[@id = 'menu']/li/descendant::a[contains(string(.), 'Home')]
DomCrawler
DomCrawler
DomCrawler Introduction
Le composant DomCrawler offre une API simple pour analyser
un document SGML grâce au support intégral des sélecteurs
CSS3 et XPath.
DomCrawler Introduction § Analyser et extraire des informations d’un document SGML
§ Supporte différentes sources de données
Ø Chaîne HTML ou XML
Ø Objet DOMNode ou DOMNodeList
Ø Objet DOMDocument
§ Supporte les sélecteurs CSS3 et Xpath
§ Crawler s’appuie sur le composant CssSelector
DomCrawler Instancier un Crawler
use Symfony\Component\DomCrawler\Crawler; // from an HTML string $crawler = new Crawler(file_get_contents('http://www.google.com')); // from a DomDocument object $doc = new \DOMDocument(); $doc->loadHTML(file_get_contents('http://www.google.com')); $crawler = new Crawler($doc, 'http://www.google.com'); // from a DomNodeList object $list = $doc->getElementsByTagName('ul'); $crawler = new Crawler($list); // from a DomNode object $logo = $doc->getElementById('logo'); $crawler = new Crawler($logo);
DomCrawler Traverser un document /** * <div id="sidebar"> * <h2>Sidebar</h2> * <ul> * <li><a href="home.html">Home</a></li> * <li><a href="about.html">About</a></li> * <li> * <a href="contact.html">Contact</a> * </li> * </ul> * </div> */ $label = $crawler->filter('#sidebar ul li')-> last()->children()-> eq(0)->text();
DomCrawler Traverser un document
/** * <div id="sidebar"> * <h2>Sidebar</h2> * <ul> * <li><a href="home.html">Home</a></li> * <li><a href="about.html">About</a></li> * <li> * <a href="contact.html">Contact</a> * </li> * </ul> * </div> */ $links = $crawler->filter('#sidebar ul li a')->links();