Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Useful PHP libraries

E2ed7c278c8c49bb3e7fe0b7de039997?s=47 Hugo Hamon
January 25, 2013

Useful PHP libraries

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 and noSQL databases, checking and fixing code style violations, testing your code…

E2ed7c278c8c49bb3e7fe0b7de039997?s=128

Hugo Hamon

January 25, 2013
Tweet

Transcript

  1. There is a library for that! Hugo HAMON – PHPBenelux

    2013 - Antwerp http://www.flickr.com/photos/pfranche/4686103974
  2. None
  3. None
  4. http://octodex.github.com/gangnamtocat.md/

  5. None
  6. None
  7. The PHP « app » store J

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

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

  13. Thumbnailing a picture

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

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

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

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

  20. 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);
  21. Paginating a doctrine query $qb = $em ->createQueryBuilder() ->select('u') ->from('Model\Article',

    'u') ; $adapter = new DoctrineORMAdapter($qb); $pager = new Pagerfanta($adapter); $pager->setMaxPerPage(10);
  22. $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();
  23. Guzzle http://www.flickr.com/photos/lachlanbird/4407427427

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

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

    Geocoder\Dumper\GpxDumper(); $xml = $dumper->dump($geo);
  32. 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>
  33. Faker http://www.flickr.com/photos/mgildberg/3855090253

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

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

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

    array * @Secure * @Method("POST") */
  40. 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 ));
  41. 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; } }
  42. namespace Acme\Controller; use Acme\Annotation\Method; class BlogController { /** * @Method(methods

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

  45. Object Relational Mapping

  46. $talk

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

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

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

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

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

  57. 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' ) ;
  58. 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) ;
  59. 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);
  60. Twig http://www.flickr.com/photos/art_roman_p/4356871215

  61. <?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
  62. Concise Syntax {# ... comment something ... #} {% ...

    do something ... %} {{ ... display something ... }}
  63. 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
  64. 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.
  65. 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 %}
  66. {% 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
  67. Pimpl e

  68. Global Con guration + Lazy Services = Container

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

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

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

  74. $ 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
  75. Before

  76. After

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

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

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

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

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

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