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

Useful PHP libraries

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…

Hugo Hamon

January 25, 2013
Tweet

More Decks by Hugo Hamon

Other Decks in Technology

Transcript

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

    2013 - Antwerp http://www.flickr.com/photos/pfranche/4686103974
  2. 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' ));
  3. Sending an automatic email $mailer = new Monolog\Handler\NativeMailerHandler( '[email protected]', 'New

    recorded error', '[email protected]', Logger::ERROR ); $logger = new Monolog\Logger('frontend'); $logger->pushHandler($mailer); $logger->addError('Cannot find User object');
  4. 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(...));
  5. 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') ;
  6. 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') ;
  7. Flipping an image use Imagine\Gd\Imagine; $imagine = new Imagine(); $imagine

    ->open(__DIR__.'/elephpant.black.png') ->flipVertically() ->save(__DIR__.'/elephpant.flip.png') ;
  8. 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);
  9. Paginating a doctrine query $qb = $em ->createQueryBuilder() ->select('u') ->from('Model\Article',

    'u') ; $adapter = new DoctrineORMAdapter($qb); $pager = new Pagerfanta($adapter); $pager->setMaxPerPage(10);
  10. 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();
  11. Executing POST HTTP Requests use Guzzle\Service\Client; $client = new Client('http://website.domain.tld');

    $client ->post('/contact', null, array( 'email' => '[email protected]', 'message' => 'Some contact message.', )) ->send() ;
  12. 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());
  13. 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] => )
  14. 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());
  15. 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] => )
  16. Dumping GPS coordinates $geo = $geocoder->reverse(48.9013, 2.3173); $dumper = new

    Geocoder\Dumper\GpxDumper(); $xml = $dumper->dump($geo);
  17. 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>
  18. 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
  19. 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"; }
  20. Full name: Mr. Emerald O'Reilly Address: 2490 Johnson Springs Kelvinton,

    DC 37948-6551 Email: [email protected] Phone: 08904319602 Password: 50593818 ---------------- Full name: Lorenza Funk Address: 7105 Hoeger Burg East Jarret, MI 01719-1324 Email: [email protected] Phone: 705-084-6766 Password: 22536248
  21. /** * This is a doc bloc * * @return

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

    = { "POST", "GET" }) */ public function editAction() { echo 'Editing blog post...'; } }
  24. 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
  25. /** @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; }
  26. Persisting Objects to a Database $talk = new Talk(); $talk->setTitle('Doctrine

    ORM'); $talk->addSpeaker(new Speaker('hhamon'); $em->persist($talk); $em->flush();
  27. 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() ;
  28. 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();
  29. 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'), ));
  30. 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();
  31. Creating a basic message $message = Swift_Message::newInstance() ->setSubject('Your subject') ->setFrom(array('[email protected]'

    => 'John Doe')) ->setTo(array('[email protected]', '[email protected]')) ->setBody('Here is the message itself') ->addPart( '<q>Here is the message itself</q>', 'text/html' ) ;
  32. Sending the message with SMTP $transport = Swift_SmtpTransport::newInstance() ->setHost('smtp.gmail.com') ->setUsername('[email protected]')

    ->setPassword('your password') ->setPort(25) ->setEncryption('ssl') ; $mailer = Swift_Mailer::newInstance($transport); $result = $mailer->send($message);
  33. <?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
  34. Concise Syntax {# ... comment something ... #} {% ...

    do something ... %} {{ ... display something ... }}
  35. 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
  36. 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.
  37. 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 %}
  38. {% 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
  39. 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*';
  40. 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'] ); });
  41. Requesting a service $message = Message(); $message->setFrom('[email protected]'); // ... //

    Mailer and transport services are // created on demand $pimple['mailer']->send($message);
  42. $ 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
  43. 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 )
  44. 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));
  45. 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');
  46. 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'));
  47. The Crawler API $filter = function ($node, $i) { $content

    = (string) $node->textContent; if (!preg_match('/symfony/i', $content)) { return false; } }; $tweets = $crawler->reduce($filter);
  48. 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'));