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

There is a Library for That!

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

Hugo Hamon

November 03, 2012
Tweet

More Decks by Hugo Hamon

Other Decks in Technology

Transcript

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

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

  11. Thumbnailing a picture

    View Slide

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

    View Slide

  13. Changing the Background Color

    View Slide

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

    View Slide

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

    View Slide

  16. Flipping an image

    View Slide

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

    View Slide

  18. Using effects

    View Slide

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

    View Slide

  20. Drawing shapes

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  30. Dumping GPS coordinates




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




    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

  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

    View Slide

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

    View Slide

  43. Object
    Relational
    Mapping

    View Slide

  44. $talk

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  53. Serving Javascripts Files



    <br/>


    Assetic Demo


    View Slide

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

    View Slide

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

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

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

    View Slide

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

    View Slide












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

    View Slide

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

    View Slide

  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

    View Slide

  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.

    View Slide

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

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

  65. Pimpl e

    View Slide

  66. Global Con guration
    + Lazy Services
    = Container

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  71. PHP CS Fixer

    View Slide

  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

    View Slide

  73. Before

    View Slide

  74. After

    View Slide

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

    View Slide

  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
    )

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

  84. http://www.flickr.com/photos/eyetwist/7466949276

    View Slide

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

    View Slide