125
Symfony2 en pièces détachées Symfony Live 2011 - 4 mars 2011 - Paris

Symfony2 en pièces détachées

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

Page 1: Symfony2 en pièces détachées

Symfony2 en pièces détachées

Symfony Live 2011 - 4 mars 2011 - Paris

Page 2: Symfony2 en pièces détachées

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

Page 3: Symfony2 en pièces détachées

Avant d’oublier…

http://joind.in/talk/view/2766

Page 4: Symfony2 en pièces détachées

Quelques questions…

Qui développe encore des applications sans framework ou bibliothèques ?

Page 5: Symfony2 en pièces détachées

Quelques questions…

Qui réutilise des composants éprouvés ? Zend Framework, PEAR, EZ Components…

Page 6: Symfony2 en pièces détachées

Quelques questions…

Qui réinvente à la roue ?

Page 7: Symfony2 en pièces détachées

Symfony2 est bâti autour d’une bibliothèque de composants indépendants

Page 8: Symfony2 en pièces détachées

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

Page 9: Symfony2 en pièces détachées

ClassLoader

ClassLoader

Page 10: Symfony2 en pièces détachées

ClassLoader

UniversalClassLoader

Page 11: Symfony2 en pièces détachées

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

Page 12: Symfony2 en pièces détachées

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

Page 13: Symfony2 en pièces détachées

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

Page 14: Symfony2 en pièces détachées

HttpFoundation

HttpFoundation

Page 15: Symfony2 en pièces détachées

HttpFoundation

Request

Page 16: Symfony2 en pièces détachées

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

Page 17: Symfony2 en pièces détachées

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

Page 18: Symfony2 en pièces détachées

HttpFoundation

Session

Page 19: Symfony2 en pièces détachées

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

Page 20: Symfony2 en pièces détaché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!');

Page 21: Symfony2 en pièces détaché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 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();

Page 22: Symfony2 en pièces détachées

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');

Page 23: Symfony2 en pièces détachées

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;

Page 24: Symfony2 en pièces détachées

HttpFoundation

Response

Page 25: Symfony2 en pièces détachées

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

Page 26: Symfony2 en pièces détachées

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();

Page 27: Symfony2 en pièces détachées

HttpFoundation

Cookie

Page 28: Symfony2 en pièces détachées

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();

Page 29: Symfony2 en pièces détachées

HttpFoundation

File

Page 30: Symfony2 en pièces détachées

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="" />';

Page 31: Symfony2 en pièces détachées

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'); }

Page 32: Symfony2 en pièces détachées

Event Dispatcher

Event Dispatcher

Page 33: Symfony2 en pièces détachées

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

Page 34: Symfony2 en pièces détachées

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); } }

Page 35: Symfony2 en pièces détachées

Event Dispatcher

« Forcer le nettoyage d’un cache lorsque certains événements se

produisent »

Page 36: Symfony2 en pièces détachées

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…

Page 37: Symfony2 en pièces détachées

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.

Page 38: Symfony2 en pièces détachées

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.

Page 39: Symfony2 en pièces détachées

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);

Page 40: Symfony2 en pièces détachées

Event Dispatcher

« Filtrer / transformer une valeur »

Page 41: Symfony2 en pièces détachées

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); } }

Page 42: Symfony2 en pièces détachées

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

Page 43: Symfony2 en pièces détachées

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 »).

Page 44: Symfony2 en pièces détachées

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>.');

Page 45: Symfony2 en pièces détachées

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

Page 46: Symfony2 en pièces détachées

Dependency Injection

Dependency Injection

Page 47: Symfony2 en pièces détachées

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

Page 48: Symfony2 en pièces détachées

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.  

Page 49: Symfony2 en pièces détachées

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  

Page 50: Symfony2 en pièces détachées

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>

Page 51: Symfony2 en pièces détachées

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  

Page 52: Symfony2 en pièces détachées

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é.  

Page 53: Symfony2 en pièces détachées

Finder

Finder

Page 54: Symfony2 en pièces détachées

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

Page 55: Symfony2 en pièces détachées

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__);

Page 56: Symfony2 en pièces détachées

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__);

Page 57: Symfony2 en pièces détachées

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(); }

Page 58: Symfony2 en pièces détachées

Templating

Templating

Page 59: Symfony2 en pièces détachées

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

Page 60: Symfony2 en pièces détachées

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'));

Page 61: Symfony2 en pièces détachées

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>

Page 62: Symfony2 en pièces détachées

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.

Page 63: Symfony2 en pièces détachées

# 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

Page 64: Symfony2 en pièces détachées

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 :

Page 65: Symfony2 en pièces détachées

Translation

Translation

Page 66: Symfony2 en pièces détachées

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

Page 67: Symfony2 en pièces détachées

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>

Page 68: Symfony2 en pièces détachées

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

Page 69: Symfony2 en pièces détachées

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>

Page 70: Symfony2 en pièces détachées

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'));

Page 71: Symfony2 en pièces détachées

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>

Page 72: Symfony2 en pièces détachées

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>') );

Page 73: Symfony2 en pièces détachées

Locale

Locale

Page 74: Symfony2 en pièces détachées

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

Page 75: Symfony2 en pièces détachées

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'));

Page 76: Symfony2 en pièces détachées

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());

Page 77: Symfony2 en pièces détachées

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'));

Page 78: Symfony2 en pièces détachées

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());

Page 79: Symfony2 en pièces détachées

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'));

Page 80: Symfony2 en pièces détachées

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());

Page 81: Symfony2 en pièces détachées

Console

Console

Page 82: Symfony2 en pièces détachées

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

Page 83: Symfony2 en pièces détachées

Console Introduction

§  Arguments et options

§  Codes de sortie

§  Shell

§  Coloration de la sortie

§  Testabilité

§  Messages d’erreur

§  …

Page 84: Symfony2 en pièces détachées

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)

Page 85: Symfony2 en pièces détachées

Console Amorcer le CLI

Page 86: Symfony2 en pièces détachées

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…

Page 87: Symfony2 en pièces détachées

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

Page 88: Symfony2 en pièces détachées

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();  

Page 89: Symfony2 en pièces détachées

Console Consulter la liste des commandes

Page 90: Symfony2 en pièces détachées

Console Obtenir de l’aide de la commande

./console help google:weather  

Page 91: Symfony2 en pièces détachées

Console Exécuter la commande

./console google:weather Berlin --lang fr  

Page 92: Symfony2 en pièces détachées

Process

Process

Page 93: Symfony2 en pièces détachées

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

Page 94: Symfony2 en pièces détachées

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;

Page 95: Symfony2 en pièces détachées

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

Page 96: Symfony2 en pièces détachées

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;

Page 97: Symfony2 en pièces détachées

Serializer

Serializer

Page 98: Symfony2 en pièces détachées

Serializer Introduction

Le composant « Serializer » convertit des objets PHP en

représentations XML ou JSON par introspection.

Page 99: Symfony2 en pièces détachées

Serializer Architecture

Page 100: Symfony2 en pièces détachées

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.

Page 101: Symfony2 en pièces détachées

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; } }

Page 102: Symfony2 en pièces détachées

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; } }

Page 103: Symfony2 en pièces détachées

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');

Page 104: Symfony2 en pièces détachées

Serializer Sérialiser un objet en XML

Page 105: Symfony2 en pièces détachées

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');

Page 106: Symfony2 en pièces détachées

Serializer Sérialiser un objet en JSON

{ "title":"Ten Little Niggers", "author": { "firstname":"Agatha", "lastname":"Christie", "fullname":"Agatha Christie" } }

Page 107: Symfony2 en pièces détachées

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'));

Page 108: Symfony2 en pièces détachées

Serializer Désérialiser un objet JSON

Array ( [title] => Ten Little Niggers [author] => Array ( [firstname] => Agatha [lastname] => Christie [fullname] => Agatha Christie ) )

Page 109: Symfony2 en pièces détachées

YAML

YAML

Page 110: Symfony2 en pièces détachées

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.

Page 111: Symfony2 en pièces détachées

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: ~

Page 112: Symfony2 en pièces détachées

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.

Page 113: Symfony2 en pièces détachées

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] => ) ) )

Page 114: Symfony2 en pièces détachées

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

Page 115: Symfony2 en pièces détachées

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 }

Page 116: Symfony2 en pièces détachées

CSS Selector

CSS Selector

Page 117: Symfony2 en pièces détachées

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')); }

Page 118: Symfony2 en pièces détachées

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')]

Page 119: Symfony2 en pièces détachées

DomCrawler

DomCrawler

Page 120: Symfony2 en pièces détachées

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.

Page 121: Symfony2 en pièces détachées

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

Page 122: Symfony2 en pièces détachées

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);

Page 123: Symfony2 en pièces détachées

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();

Page 124: Symfony2 en pièces détachées

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();

Page 125: Symfony2 en pièces détachées

The End…

Questions ?

http://joind.in/talk/view/2766

[email protected] @hhamon