Slide 1

Slide 1 text

There is a library for that! Hugo HAMON – PHPBenelux 2013 - Antwerp http://www.flickr.com/photos/pfranche/4686103974

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

http://octodex.github.com/gangnamtocat.md/

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

The PHP « app » store J

Slide 8

Slide 8 text

Monolog http://www.flickr.com/photos/kmaciag/5768389773

Slide 9

Slide 9 text

Logging to a le use Monolog\Logger; use Monolog\Handler\StreamHandler; $stream = new StreamHandler('dev.log', Logger::DEBUG); $logger = new Logger('frontend'); $logger->pushHandler($stream); $logger->addInfo('Authenticating user', array( 'username' => 'hhamon' ));

Slide 10

Slide 10 text

Sending an automatic email $mailer = new Monolog\Handler\NativeMailerHandler( 'hhamon@example.com', 'New recorded error', 'alert@domain.tldr', Logger::ERROR ); $logger = new Monolog\Logger('frontend'); $logger->pushHandler($mailer); $logger->addError('Cannot find User object');

Slide 11

Slide 11 text

Combining handlers together use Monolog\Logger; use Monolog\Handler\StreamHandler; use Monolog\Handler\FirePHPHandler; use Monolog\Handler\NativeMailerHandler; $logger = new Logger('frontend'); $logger->pushHandler(new StreamHandler(...)); $logger->pushHandler(new FirePHPHandler()); $logger->pushHandler(new NativeMailerHandler(...));

Slide 12

Slide 12 text

Imagine http://www.flickr.com/photos/dimitrisotiropoulos/4204766418

Slide 13

Slide 13 text

Thumbnailing a picture

Slide 14

Slide 14 text

Thumbnailing a picture use Imagine\Gd\Imagine; use Imagine\Image\Box; $imagine = new Imagine(); $imagine ->open(__DIR__.'/elephpant.png') ->thumbnail(new Box(120, 120)) ->save(__DIR__.'/elephpant.thumb.png') ;

Slide 15

Slide 15 text

Changing the Background Color

Slide 16

Slide 16 text

Changing the Background Color use Imagine\Image\Box; use Imagine\Image\Color; use Imagine\Image\Point; $imagine = new \Imagine\Gd\Imagine(); $thumb = $imagine->open(__DIR__.'/elephpant.thumb.png'); $size = $thumb->getSize(); // Apply a black background $imagine ->create(new Box($size->getWidth(), $size->getHeight()), new Color('#000')) ->paste($thumb, new Point(0, 0)) ->save(__DIR__.'/elephpant.black.png') ;

Slide 17

Slide 17 text

Flipping an image use Imagine\Gd\Imagine; $imagine = new Imagine(); $imagine ->open(__DIR__.'/elephpant.black.png') ->flipVertically() ->save(__DIR__.'/elephpant.flip.png') ;

Slide 18

Slide 18 text

Flipping an image

Slide 19

Slide 19 text

Pagerfanta http://www.flickr.com/photos/allaneroc/4336086452

Slide 20

Slide 20 text

Paginating an array use Pagerfanta\Pagerfanta; use Pagerfanta\Adapter\ArrayAdapter; $posts = array( array('id' => 4, 'title' => '...'), array('id' => 7, 'title' => '...'), [...] ); $pager = new Pagerfanta(new ArrayAdapter($array)); $pager->setMaxPerPage(10);

Slide 21

Slide 21 text

Paginating a doctrine query $qb = $em ->createQueryBuilder() ->select('u') ->from('Model\Article', 'u') ; $adapter = new DoctrineORMAdapter($qb); $pager = new Pagerfanta($adapter); $pager->setMaxPerPage(10);

Slide 22

Slide 22 text

$pager->setMaxPerPage($maxPerPage); $pager->getMaxPerPage(); $pager->setCurrentPage($currentPage); $pager->getCurrentPage(); $pager->getNbPages(); $pager->getNbResults(); $pager->getCurrentPageResults(); $pager->haveToPaginate(); $pager->hasPreviousPage(); $pager->getPreviousPage(); $pager->hasNextPage(); $pager->getNextPage();

Slide 23

Slide 23 text

Guzzle http://www.flickr.com/photos/lachlanbird/4407427427

Slide 24

Slide 24 text

Executing GET HTTP Requests use Guzzle\Service\Client; $client = new Client('http://weather.yahooapis.com'); $response = $client ->get('/forecastrss?w=615702&u=c') ->send() ; $xml = $response->getBody();

Slide 25

Slide 25 text

Executing POST HTTP Requests use Guzzle\Service\Client; $client = new Client('http://website.domain.tld'); $client ->post('/contact', null, array( 'email' => 'me@example.com', 'message' => 'Some contact message.', )) ->send() ;

Slide 26

Slide 26 text

Geocoder http://www.flickr.com/photos/fenti/3450436058

Slide 27

Slide 27 text

Geocoding an address use Geocoder\Geocoder; use Geocoder\HttpAdapter\CurlHttpAdapter; use Geocoder\Provider\OpenStreetMapsProvider; $http = new CurlHttpAdapter(); $osm = new OpenStreetMapsProvider($http); $geocoder = new Geocoder(); $geocoder->registerProviders(array($osm)); $result = $geocoder->geocode('92 Boulevard Victor Hugo, Clichy'); var_dump($result->toArray());

Slide 28

Slide 28 text

Array( [latitude] => 48.9013792 [longitude] => 2.3173131 [bounds] => Array( [south] => 48.891378631592 [west] => 2.3073129558563 [north] => 48.911382446289 [east] => 2.3273131942749 ) [streetNumber] => 92-98 [streetName] => Boulevard Victor Hugo [zipcode] => 92110 [city] => Clichy [cityDistrict] => [county] => Hauts-De-Seine [region] => Île-De-France [regionCode] => [country] => France [countryCode] => fr [timezone] => )

Slide 29

Slide 29 text

Reversing coordinates use Geocoder\Geocoder; use Geocoder\HttpAdapter\CurlHttpAdapter; use Geocoder\Provider\OpenStreetMapsProvider; $http = new CurlHttpAdapter(); $osm = new OpenStreetMapsProvider($http); $geocoder = new Geocoder(); $geocoder->registerProviders(array($osm)); $result = $geocoder->reverse(48.9013792, 2.3173131); print_r($result->toArray());

Slide 30

Slide 30 text

Array ( [latitude] => 48.9013792 [longitude] => 2.3173131 [bounds] => [streetNumber] => 92-98 [streetName] => Boulevard Victor Hugo [zipcode] => 92110 [city] => Clichy [cityDistrict] => [county] => Hauts-De-Seine [region] => Île-De-France [regionCode] => [country] => France [countryCode] => fr [timezone] => )

Slide 31

Slide 31 text

Dumping GPS coordinates $geo = $geocoder->reverse(48.9013, 2.3173); $dumper = new Geocoder\Dumper\GpxDumper(); $xml = $dumper->dump($geo);

Slide 32

Slide 32 text

Dumping GPS coordinates

Slide 33

Slide 33 text

Faker http://www.flickr.com/photos/mgildberg/3855090253

Slide 34

Slide 34 text

Supported data providers § Full names § Phone numbers § Emails & IPs § User agents § Titles § IBSN codes § Hashes (md5, sha1…) § Dates and times § URLs § Paragraphs § Sentences § Postal addresses

Slide 35

Slide 35 text

Generating fake data $faker = Faker\Factory::create(); for ($i = 0; $i < 3; $i++) { echo 'Full name: ', $faker->name ,"\n"; echo "Address:\n"; echo $faker->address, "\n"; echo 'Email: ', $faker->email, "\n"; echo 'Phone: ', $faker->phoneNumber, "\n"; echo 'Password: ', $faker->randomNumber(8), "\n"; echo "----------------\n"; }

Slide 36

Slide 36 text

Full name: Mr. Emerald O'Reilly Address: 2490 Johnson Springs Kelvinton, DC 37948-6551 Email: hills.hailee@swifttorphy.org Phone: 08904319602 Password: 50593818 ---------------- Full name: Lorenza Funk Address: 7105 Hoeger Burg East Jarret, MI 01719-1324 Email: judy.grimes@nolan.com Phone: 705-084-6766 Password: 22536248

Slide 37

Slide 37 text

Doctrine Annotations Parser http://www.flickr.com/photos/joanplanas/902644892

Slide 38

Slide 38 text

An annotation describes a code behavior that is triggered at runtime.

Slide 39

Slide 39 text

/** * This is a doc bloc * * @return array * @Secure * @Method("POST") */

Slide 40

Slide 40 text

Bootstrapping the Annotation Reader use Doctrine\Common\Annotations\IndexedReader; use Doctrine\Common\Annotations\FileCacheReader; use Doctrine\Common\Annotations\AnnotationReader; $reader = new IndexedReader(new FileCacheReader( new AnnotationReader(), __DIR__.'/cache', $debug = true ));

Slide 41

Slide 41 text

Annotations are Classes namespace Acme\Annotation; /** * @Annotation * @Target({ "METHOD" }) */ class Method { /** @var array */ private $methods; public function __construct(array $options) { $this->methods = $options['methods']; } public function getMethods() { return $this->methods; } }

Slide 42

Slide 42 text

namespace Acme\Controller; use Acme\Annotation\Method; class BlogController { /** * @Method(methods = { "POST", "GET" }) */ public function editAction() { echo 'Editing blog post...'; } }

Slide 43

Slide 43 text

namespace Acme; class FrontController { public function dispatch(Request $request) { $reader = $this->getAnnotationReader(); # Acme\\Controller\\BlogController::indexAction $controller = $request->getController(); $annot = $reader->getMethodAnnotation( new \ReflectionMethod($controller), 'Acme\Annotation\Method' ); if ($annot && !in_array($request->getMethod(), $annot->getMethods())) { throw new HttpMethodNotAllowedException(); } // ... execute controller } } Behavior

Slide 44

Slide 44 text

Doctrine ORM http://www.flickr.com/photos/7623944@N03/3922714383

Slide 45

Slide 45 text

Object Relational Mapping

Slide 46

Slide 46 text

$talk

Slide 47

Slide 47 text

/** @ORM\Entity() */ class Talk { /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** @ORM\Column(length=80) */ private $title; /** @ORM\ManyToMany(targetEntity="Speaker", mappedBy="talks") */ private $speakers; }

Slide 48

Slide 48 text

Persisting Objects to a Database $talk = new Talk(); $talk->setTitle('Doctrine ORM'); $talk->addSpeaker(new Speaker('hhamon'); $em->persist($talk); $em->flush();

Slide 49

Slide 49 text

Querying the Database $talks = $em ->createQueryBuilder() ->from('Talk', 't') ->select('t, s') ->leftJoin('t.speakers', 's') ->where('s.name = :name') ->setParameter('name', 'hhamon') ->getQuery() ->getResult() ;

Slide 50

Slide 50 text

Assetic http://www.flickr.com/photos/7593077@N03/2650612822

Slide 51

Slide 51 text

Assetic in Action Merge Filter Cache Image JavaScript Stylesheet Assetic Collection

Slide 52

Slide 52 text

Merging Javascripts Files use Assetic\Asset\AssetCollection; use Assetic\Asset\FileAsset; use Assetic\Asset\GlobAsset; $js = new AssetCollection(array( new FileAsset(__DIR__.'/jquery.js'), new GlobAsset(__DIR__.'/js/*.js'), )); header('Content-Type: application/js'); echo $js->dump();

Slide 53

Slide 53 text

Compressing Javascripts Files use Assetic\Asset\AssetCollection; use Assetic\Asset\FileAsset; use Assetic\Asset\GlobAsset; use Assetic\Filter\Yui\JsCompressorFilter; $js = new AssetCollection(array( new FileAsset(__DIR__.'/jquery.js'), new GlobAsset(__DIR__.'/js/*.js'), ), array( new JsCompressorFilter(__DIR__.'/yuicompressor.jar'), ));

Slide 54

Slide 54 text

Adding some HTTP caching goodnesses $js = new AssetCollection(...); $mtime = gmdate( 'D, d M y H:i:s \\G\\M\\T', $js->getLastModified() ); if ($mtime === $_SERVER['HTTP_IF_MODIFIED_SINCE']) { header('HTTP/1.0 304 Not Modified'); exit; } header('Content-Type: text/javascript'); header('Last-Modified: '.$mtime); echo $js->dump();

Slide 55

Slide 55 text

Serving Javascripts Files

Assetic Demo

Slide 56

Slide 56 text

Swiftmailer http://www.flickr.com/photos/andersofsydney/6909863725

Slide 57

Slide 57 text

Creating a basic message $message = Swift_Message::newInstance() ->setSubject('Your subject') ->setFrom(array('john@doe.com' => 'John Doe')) ->setTo(array('jen@domain.org', 'jin@domain.org')) ->setBody('Here is the message itself') ->addPart( 'Here is the message itself', 'text/html' ) ;

Slide 58

Slide 58 text

Attaching les $file = Swift_Attachment::fromPath('/path/to/ file.pdf'); $message ->setSubject('Your subject') ->setFrom(array('john@doe.com' => 'John Doe')) ->setTo(array('receiver@domain.org')) ->setBody('Here is the message itself') ->attach($file) ;

Slide 59

Slide 59 text

Sending the message with SMTP $transport = Swift_SmtpTransport::newInstance() ->setHost('smtp.gmail.com') ->setUsername('you@gmail.com') ->setPassword('your password') ->setPort(25) ->setEncryption('ssl') ; $mailer = Swift_Mailer::newInstance($transport); $result = $mailer->send($message);

Slide 60

Slide 60 text

Twig http://www.flickr.com/photos/art_roman_p/4356871215

Slide 61

Slide 61 text

Contextual content Manual escaping No isolation No separation of concerns

Slide 62

Slide 62 text

Concise Syntax {# ... comment something ... #} {% ... do something ... %} {{ ... display something ... }}

Slide 63

Slide 63 text

Accurate Debugging Hello {{ rand(['John', 'Tom', 'Paul']) }}! Twig_Error_Syntax: The function "rand" does not exist. Did you mean "random" in "hello.twig" at line 3

Slide 64

Slide 64 text

Variables Abstraction {{ article.title }} The article can be an array or an object. The title can be a key of the array, a public property, a regular method or a getter method.

Slide 65

Slide 65 text

layout.twig {% block body %} {% endblock body %} {% block breadcrumb %} ... {% endblock %} blog.twig {% extends "layout.twig" %} {% block breadcrumb %} {{ parent() }} - Blog {% endblock breadcrumb %} {% block body %}

Latest posts

{% include "posts.twig" %} {% endblock body %}

Slide 66

Slide 66 text

{% extends "layout.twig" %} {% block breadcrumb %} {{ parent() }} - Blog {% endblock breadcrumb %} {% block body %}

Latest posts

{% include "posts.twig" %} {% endblock body %} Reuse the parent block default value. Fill the parent body block. Template Inheritance

Slide 67

Slide 67 text

Pimpl e

Slide 68

Slide 68 text

Global Con guration + Lazy Services = Container

Slide 69

Slide 69 text

Con guring global parameters $pimple = new Pimple(); $pimple['transport.smtp.host'] = 'smtp.foo.com'; $pimple['transport.smtp.port'] = 1234; $pimple['transport.smtp.user'] = 'mailer'; $pimple['transport.smtp.pwd'] = '^p4$$W0rD*';

Slide 70

Slide 70 text

Registering some services $pimple['mailer.transport'] = $pimple->share( function ($c) { return new SMTPTransport( $c['transport.smtp.host'], $c['transport.smtp.port'], $c['transport.smtp.user'], $c['transport.smtp.pwd'] ); });

Slide 71

Slide 71 text

Registering some services $pimple['mailer'] = $pimple->share( function ($c) { return new Mailer($c['mailer.transport']); });

Slide 72

Slide 72 text

Requesting a service $message = Message(); $message->setFrom('me@example.com'); // ... // Mailer and transport services are // created on demand $pimple['mailer']->send($message);

Slide 73

Slide 73 text

PHP CS Fixer

Slide 74

Slide 74 text

$ php php-cs-fixer.phar fix /path/to/project --level=psr0 $ php php-cs-fixer.phar fix /path/to/project --level=psr1 $ php php-cs-fixer.phar fix /path/to/project --level=psr2 $ php php-cs-fixer.phar fix /path/to/project --level=all

Slide 75

Slide 75 text

Before

Slide 76

Slide 76 text

After

Slide 77

Slide 77 text

Plum Deployment http://www.flickr.com/photos/nathaliedenis/7805013768

Slide 78

Slide 78 text

De ning servers $options = array( 'commands' => array( 'php app/console assets:install', 'php app/console cache:clear --quiet --no-debug', ), ); $server = new Plum\Server\Server('123.215.108.198', 'username', '/var/www/domain.com', 'password', 22, $options )

Slide 79

Slide 79 text

Registering deployers use Plum\Plum; use Plum\Deployer\RsyncDeployer; use Plum\Deployer\SshDeployer; $plum = new Plum(); $plum->addServer('production', $server); $plum->registerDeployer(new RsyncDeployer()); $plum->registerDeployer(new SshDeployer()); $plum->setOptions(array('dry_run' => true));

Slide 80

Slide 80 text

Deploying # $plum->deploy('server', 'deployer'); $plum->deploy('production', 'rsync'); $plum->deploy('production', 'ssh');

Slide 81

Slide 81 text

Goutte http://www.flickr.com/photos/by-corsu/3886088069

Slide 82

Slide 82 text

Using the Goutte Client use Goutte\Client; $client = new Client(); $client->setHeader('Cookie', 'name=hhamon'); $client->setAuth('user', 'password', 'basic'); $crawler = $client->request('GET', 'http://foo.com');

Slide 83

Slide 83 text

The Crawler API // Filtering with CSS or XPath selectors $tweets = $crawler->filter('#sidebar .tweet'); $tweets = $crawler->filterXPath('//p[class="tweet"]'); // Traversing $first = $tweets->first(); $third = $tweets->eq(2); $last = $tweets->last(); // Extracting $text = $first->text(); $class = $first->attr('class'); $infos = $first->extract(array('_text', 'class'));

Slide 84

Slide 84 text

The Crawler API $filter = function ($node, $i) { $content = (string) $node->textContent; if (!preg_match('/symfony/i', $content)) { return false; } }; $tweets = $crawler->reduce($filter);

Slide 85

Slide 85 text

Finding links and forms $link = $crawler->selectLink('Click me')->link(); $crawler = $client->click($link); $form = $crawler->selectButton('send')->form(); $client->submit($form, array('name' => 'Foo'));

Slide 86

Slide 86 text

http://www.flickr.com/photos/eyetwist/7466949276 Conclusion…

Slide 87

Slide 87 text

https://joind.in/talk/view/7789