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

    View Slide

  2. View Slide

  3. View Slide

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

    View Slide

  5. View Slide

  6. View Slide

  7. The PHP « app » store J

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. Thumbnailing a picture

    View Slide

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

    View Slide

  15. Changing the Background Color

    View Slide

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

    View Slide

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

    View Slide

  18. Flipping an image

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. Dumping GPS coordinates




    92110, Clichy, Hauts-De-Seine, Île-De-France, France]]>




    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  44. Doctrine ORM
    http://www.flickr.com/photos/[email protected]/3922714383

    View Slide

  45. Object
    Relational
    Mapping

    View Slide

  46. $talk

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  50. Assetic
    http://www.flickr.com/photos/[email protected]/2650612822

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. Serving Javascripts Files



    <br/>


    Assetic Demo


    View Slide

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

    View Slide

  57. 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(
    'Here is the message itself',
    'text/html'
    )
    ;

    View Slide

  58. Attaching les
    $file = Swift_Attachment::fromPath('/path/to/
    file.pdf');
    $message
    ->setSubject('Your subject')
    ->setFrom(array('[email protected]' => 'John Doe'))
    ->setTo(array('[email protected]'))
    ->setBody('Here is the message itself')
    ->attach($file)
    ;

    View Slide

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

    View Slide

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

    View Slide












  61. Contextual content
    Manual escaping
    No isolation
    No separation of
    concerns

    View Slide

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

    View Slide

  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

    View Slide

  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.

    View Slide

  65. 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 %}

    View Slide

  66. {% 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

    View Slide

  67. Pimpl e

    View Slide

  68. Global Con guration
    + Lazy Services
    = Container

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  73. PHP CS Fixer

    View Slide

  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

    View Slide

  75. Before

    View Slide

  76. After

    View Slide

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

    View Slide

  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
    )

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide