There is a Library for That!

E2ed7c278c8c49bb3e7fe0b7de039997?s=47 Hugo Hamon
November 03, 2012

There is a Library for That!

Reinventing the wheel is always boring and by chance the PHP community is very prolific. Looking for an already made quality PHP library remains sometimes difficult. But thanks to Github, PEAR and Packagist, it's now very easy to dig a little and find powerful and well written PHP libraries.

This session will introduce some of the best PHP libraries for manipulating images, abstracting an HTTP client, manipulating relational databases, checking and fixing code style violations, deploying files...

E2ed7c278c8c49bb3e7fe0b7de039997?s=128

Hugo Hamon

November 03, 2012
Tweet

Transcript

  1. There is a library for that! http://www.flickr.com/photos/safaripartners/4839030908 https://joind.in/talk/view/7417

  2. None
  3. None
  4. None
  5. None
  6. Monolog http://www.flickr.com/photos/kmaciag/5768389773

  7. 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' ));
  8. 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');
  9. 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(...));
  10. Imagine http://www.flickr.com/photos/dimitrisotiropoulos/4204766418

  11. Thumbnailing a picture

  12. 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') ;
  13. Changing the Background Color

  14. 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') ;
  15. Flipping an image use Imagine\Gd\Imagine; $imagine = new Imagine(); $imagine

    ->open(__DIR__.'/elephpant.black.png') ->flipVertically() ->save(__DIR__.'/elephpant.flip.png') ;
  16. Flipping an image

  17. Using effects use Imagine\Gd\Imagine; $imagine = new Imagine(); $image =

    $imagine->open(__DIR__.'/elephpant.flip.png'); $image ->effects() ->negative() ->gamma(1.3) ; $image->save(__DIR__.'/elephpant.negative.png');
  18. Using effects

  19. Drawing shapes use Imagine\Gd\Imagine; use Imagine\Image\Box; use Imagine\Image\Color; use Imagine\Image\Point;

    $imagine = new Imagine(); $image = $imagine->open(__DIR__.'/elephpant.negative.png'); $image ->draw() ->ellipse(new Point(60, 60), new Box(30, 30), new Color('f00'), true) ; $image->save(__DIR__.'/elephpant.draw.png');
  20. Drawing shapes

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

  22. 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();
  23. 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() ;
  24. Geocoder http://www.flickr.com/photos/fenti/3450436058

  25. 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());
  26. 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] => )
  27. 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());
  28. 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] => )
  29. Dumping GPS coordinates $geo = $geocoder->reverse(48.9013, 2.3173); $dumper = new

    Geocoder\Dumper\GpxDumper(); $xml = $dumper->dump($geo);
  30. Dumping GPS coordinates <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <gpx version="1.0"

    creator="Geocoder" version="1.1.0"> <wpt lat="48.9013792" lon="2.3173131"> <name> <![CDATA[92-98, Boulevard Victor Hugo, 92110, Clichy, Hauts-De-Seine, Île-De-France, France]]> </name> <type><![CDATA[Address]]></type> </wpt> </gpx>
  31. Faker http://www.flickr.com/photos/mgildberg/3855090253

  32. 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
  33. 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"; }
  34. 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
  35. Doctrine Annotations Parser http://www.flickr.com/photos/joanplanas/902644892

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

    runtime.
  37. /** * This is a doc bloc * * @return

    array * @Secure * @Method("POST") */
  38. 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 ));
  39. Annotations are Classes namespace Acme\Annotation; /** * @Annotation * @Target({

    "METHOD" }) */ class Method { /** @var array<string> */ private $methods; public function __construct(array $options) { $this->methods = $options['methods']; } public function getMethods() { return $this->methods; } }
  40. namespace Acme\Controller; use Acme\Annotation\Method; class BlogController { /** * @Method(methods

    = { "POST", "GET" }) */ public function editAction() { echo 'Editing blog post...'; } }
  41. 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
  42. Doctrine ORM http://www.flickr.com/photos/7623944@N03/3922714383

  43. Object Relational Mapping

  44. $talk

  45. /** @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; }
  46. Persisting Objects to a Database $talk = new Talk(); $talk->setTitle('Doctrine

    ORM'); $talk->addSpeaker(new Speaker('hhamon'); $em->persist($talk); $em->flush();
  47. 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() ;
  48. Assetic http://www.flickr.com/photos/7593077@N03/2650612822

  49. Assetic in Action Merge Filter Cache Image JavaScript Stylesheet Assetic

    Collection
  50. 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();
  51. 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'), ));
  52. 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();
  53. Serving Javascripts Files <!DOCTYPE html> <html> <head> <script type="text/javascript" src="assets.php">

    </script> </head> <body> <h1>Assetic Demo</h1> </body> </html>
  54. Swiftmailer http://www.flickr.com/photos/andersofsydney/6909863725

  55. 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( '<q>Here is the message itself</q>', 'text/html' ) ;
  56. 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) ;
  57. 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);
  58. Twig http://www.flickr.com/photos/art_roman_p/4356871215

  59. <?php include 'header.php' ?> <?php include 'footer.php' ?> <div class="posts">

    <?php foreach ($posts as $post) : ?> <h2> <?php echo htmlspecialchars($post['title']) ?> </h2> <?php include 'post.php' ?> <?php echo substr($post['body'], 255).'...' ?> <?php endforeach ?> </div> Contextual content Manual escaping No isolation No separation of concerns
  60. Concise Syntax {# ... comment something ... #} {% ...

    do something ... %} {{ ... display something ... }}
  61. 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
  62. 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.
  63. layout.twig {% block body %} {% endblock body %} {%

    block breadcrumb %} ... {% endblock %} blog.twig {% extends "layout.twig" %} {% block breadcrumb %} {{ parent() }} - Blog {% endblock breadcrumb %} {% block body %} <h1>Latest posts</h1> {% include "posts.twig" %} {% endblock body %}
  64. {% extends "layout.twig" %} {% block breadcrumb %} {{ parent()

    }} - Blog {% endblock breadcrumb %} {% block body %} <h1>Latest posts</h1> {% include "posts.twig" %} {% endblock body %} Reuse the parent block default value. Fill the parent body block. Template Inheritance
  65. Pimpl e

  66. Global Con guration + Lazy Services = Container

  67. 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*';
  68. 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'] ); });
  69. Registering some services $pimple['mailer'] = $pimple->share( function ($c) { return

    new Mailer($c['mailer.transport']); });
  70. Requesting a service $message = Message(); $message->setFrom('me@example.com'); // ... //

    Mailer and transport services are // created on demand $pimple['mailer']->send($message);
  71. PHP CS Fixer

  72. $ 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
  73. Before

  74. After

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

  76. 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 )
  77. 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));
  78. Deploying # $plum->deploy('server', 'deployer'); $plum->deploy('production', 'rsync'); $plum->deploy('production', 'ssh');

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

  80. 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');
  81. 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'));
  82. The Crawler API $filter = function ($node, $i) { $content

    = (string) $node->textContent; if (!preg_match('/symfony/i', $content)) { return false; } }; $tweets = $crawler->reduce($filter);
  83. 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'));
  84. http://www.flickr.com/photos/eyetwist/7466949276

  85. https://joind.in/talk/view/7417