Serverside Webscripting [JLW384]

01.libraries

It looks like you are viewing these slides on GitHub. Please clone the source repository in order to get the included PHP examples to work.

Libraries & Frameworks

Welcome to the Head Museum. I'm Leonard Nimoy.

Library?

  • Set of one or more PHP Classes which you can use in your own project
  • Provide in one or several functionalities
    • Sending e-mail
    • Templating
    • Working with PDF or Word or Excel documents
    • Graphing/Charting
    • Working with Files & Folders
    • Caching
    • Data Generation
    • Database Access
    • Validation
    • etc.

Properties of a library

  • Core properties
    • Build on top of the existing PHP core functions (or an other library)
    • Include it in your own code in order to get it to work
  • Optional properties
    • Standalone
    • Structured
    • Documentation provided
    • Unit Tested
    • Extensible
    • PSR-X Compliant
      (more on that later)
Tip: you can identify a well-written library if it has most (it not all!) of the optional properties

Example Libraries

  • Specific Libraries
    • Templating: Twig
    • Sending e-mail: PHPMailer, SwiftMailer, …
    • Working with PDF files: TCPDF, FPDI, …
    • Graphing/Charting: pChart, JpGraph, Libchart, …
    • Manipulate Office Files: PHPExcel, PHPWord, …
    • Database and ORM: Doctrine, Fuel, Kohana, Propel, Redbean, …
    • Routing: Klein, Ham, jream/route, bramus/router, …
    • (#lmgtfy)
  • General Libraries
    • Spoon Library (RIP)
    • The Symfony Components

Beware though!

  • Not all codebases are created equally! Lots of cruft out there.
    • Bad quality
    • No proper abstraction
    • Legacy PHP 4 and pre-PHP 5.4 code
    • No documentation

Framework?

  • Framework > Library
    • Defines a structure to follow
    • Your project must follow that structure
    • Internally, a framework will most likely contain/use a library
  • Examples
    • Zend Framework
    • CakePHP
    • CodeIgniter
    • Laravel
    • Slim
    • Symfony
    • YII
    • Silex
    • Kohana
    • etc.

Using Libraries: Kicking it oldskool

Obtaining a library

  1. Look up the library on the internet
  2. Download it
  3. Extract it
  4. Put the correct folder somewhere in your project
  5. Include the necessary classes
  6. Use it

Updating a library

  1. Look up the website of the library again
  2. Download the new version
  3. Extract it
  4. Drop it in your project
  5. Cross your fingers and hope all still works (mostly: dependencies)
(If you do keep your code up-to-date that is)

Example: JpGraph (1)

Warning: This library is outdated (last release 2010) and is in desperate need of a code refresh!

We'll merely use it to make a few arguments later on.

Example: JpGraph (2)

<?php

// Include needed classes
require_once ('jpgraph/jpgraph.php');
require_once ('jpgraph/jpgraph_pie.php');

// Our data
$data = array(78, 22);

// Create the Pie Graph
$graph = new PieGraph(350, 300, 'auto');
$graph->SetShadow();

// Set A title for the plot + disable the border
$graph->title->Set("Percentage of chart which resembles Pac-man");
$graph->SetFrame(false);

// Create the pieplot
$pieplot = new PiePlot($data);
$pieplot->SetCenter(0.5, 0.5);
$pieplot->SetStartAngle(39);
$pieplot->SetLegends(array('Pac-man', 'Not Pac-Man'));

// Add the pieplot to the graph
$graph->Add($pieplot);

// JpGraph Bug: one must add the pieplot before setting colors
$pieplot->SetSliceColors(array('#FFFF00','#FF0000'));

// Style the Legend
$graph->legend->SetFrameWeight(0);
$graph->legend->Pos(0.5, 0.90, 'center', 'top');
$graph->legend->SetFillColor('white');
$graph->legend->SetColumns(2);

// Display the graph
$graph->Stroke();

A Better Example: SwiftMailer (1)

  • Library to send e-mail
  • Why is this library better?
    • Targetted towards PHP5
    • Comes with an autoloader
      • Just include the autoloader and whenever you use one of the Swift classes, it'll automatically require it.
      • Having an autoloader mostly attests being well-structured
    • Lots of documentation and examples
    • Unit tested

A Better Example: SwiftMailer (1)

<?php

// Include SwiftMailer Autoloader
require_once 'swiftmailer/swift_required.php';

// Create the Transport
$transport = Swift_SmtpTransport::newInstance('relay.odisee.be', 25);

// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);

// Create a message
$message = Swift_Message::newInstance('Lorem Ipsum')
	->setFrom(array('johndoe@example.org' => 'John Doe'))
	->setTo(array(
		'blackhole@bram.us' => 'blackhole'
	))
	->setBody(strip_tags(file_get_contents('assets/content.html')))
	->addPart(file_get_contents('assets/content.html'), 'text/html');

// Send it (or at least try to)
if(!$mailer->send($message, $errors)) {
	echo 'Mailer Error: ';
	print_r($errors);
} else {
	echo 'Message sent!';
}

// EOF

Intermezzo:
Autoloaders, Namespaces, & PSR-4

Autoloaders (1)

  • Essentially, an autoloader is a function which finds and loads a PHP class which has not been included yet via include or require.
    • The function takes one argument: the name of the requested class
    • Function then translates the classname to an actual file to include
    • Easy to write, if you keep your naming and structure consistent
  • Tell PHP to use that function for autoloading, using
    spl_autoload_register('nameOfTheFunction');

Autoloaders (2)

  • Example: SwiftMailer's autoloader (stripped down)
    function autoload($class) {
    	if (0 !== strpos($class, 'Swift_')) { //Don't interfere w other autoloaders
    		return;
    	}
    
    	$path = __DIR__ . '/' . str_replace('_', '/', $class) . '.php';
    
    	if (!file_exists($path)) return;
    
    	require $path;
    }
    • When using Swift_InputByteStream it'll include /path/to/swiftmailer/Swift/InputByteStream.php
    • When using Swift_Encoder_Base64Encoder it'll include /path/to/swiftmailer/Swift/Encoder/Base64Encoder.php

Namespaces (1)

  • When we talk about classes we didn't talk about namespaces
  • Namespaces allow you to logically group and name your classes
    • No more lengthy classnames such as Swift_Encoder_Base64Encoder
    • … but a class Base64Encoder in the Swift\Encoder namespace

Namespaces (2) — Demo time!

Let's take a look at the files in assets/01/examples/namespaces/

  • Define a namespace for your class at the very top of the file
    namespace Ikdoeict\Demo;
    class Foo { ... }
  • Refer to a class with its full namespace.
    $foo = new \Ikdoeict\Demo\Foo('hello-foo');
  • If you don't use the full namespace, PHP will look in the current active namespace and the – via use – imported namespaces.
    namespace Ikdoeict\Demo;
    class Bar extends Foo { ... } // = extends Ikdoeict\Demo\Foo
    use \Ikdoeict\Demo\Baz;
    $baz = new Baz('hello-baz'); // = \Ikdoeict\Demo\Baz
  • The global namespace is \
    $di = new \DirectoryIterator(__DIR__);

PSR-0

  • PSR-0 is “a standard describing mandatory requirements for autoloader interoperability”
    • Defines how to namespace your code:
      \<VendorName>\(<Namespace>\)*<ClassName>
    • Defines how that namespace translates to an organization on disk
      /path/to/project/vendor/Vendorname/Namespace/ClassName.php
  • In other words: Adhering to this folder structure makes your library play nice with the PSR-0 autoloader.
Beware: Use the exact casing on disk as some file systems are Case Sensitive!

PSR-4

  • PSR-4 is “a specification for autoloading classes from file paths”
    • PSR-0 compatible
    • Affords a more concise folder structure.
      E.g. \Vendorname\Namespace\ClassName
      • PSR-0: /path/to/project/Vendorname/Namespace/ClassName.php
      • PSR-4: /path/to/project/ClassName.php
  • Roughly translated: PSR-4 = PSR-0, redux.

Using Libraries Redux:
Composer & Packagist

Composer

Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you.
  • Roughly translated: this tool will save you lots of headaches
    • Just tell composer which package and which version (specific, or “the latest”) of a library you need and it will install/update it for you

Packagist

  • Composer is worthless without Packagist:
    • Composer = command line tool which downloads and installs the packages
    • Packagist = website aggregating all installable packages and their versions

Why Composer and not X?

  • Composer is a project specific dependency manager
    • Packages are installed on a per project basis (vendor subfolder), not system-wide (cfr. PEAR)
  • Composer is clever
    • Composer finds out which versions of which packages (and which versions of its dependencies) need to be installed, and installs them.
  • Installed packages can be autoloaded
    • Plays nice with PSR-0 and PSR-4 compliant packages
    • Composer will generate one single autoloader for all dependencies
Told you: it saves lots of headaches! ;-)

Installing Composer

  • Unix / OS X
    $ curl -s https://getcomposer.org/installer | php
    $ sudo mkdir /usr/local/bin/
    $ mv composer.phar /usr/local/bin/composer
  • Windows: Download & Run the installer

OS X users beware: If you're using MAMP, make sure that the php binary used is the one included with MAMP, and not the one shipped with OS X. (instructions)!

Installing packages using Composer: Automatic

  • Run composer require vendor/package version on the CLI
    • e.g. $ composer require swiftmailer/swiftmailer 4.*
  • Composer will:
    1. Check with packagist for its existence
    2. Write down that package requirement in composer.json
      {
          "require": {
              "swiftmailer/swiftmailer": "4.*"
          }
      }
    3. Download the package into ./vendor/vendorname/packagename
    4. Generate an autoloader for all installed packages
    5. Generate a lock file composer.lock

Installing packages using Composer: Manual

  • Create a file named composer.json in your project root to declare your dependencies in
    {
        "require": {
            "swiftmailer/swiftmailer": "4.*"
        }
    }
  • Run composer install and composer will:
    1. Check with packagist for its existence
    2. Download the package into ./vendor/vendorname/packagename
    3. Generate an autoloader for all installed packages
    4. Generate a lock file composer.lock
(note: this scenario assumes no lock file is present yet)

Using the installed packages

  • Include the Composer generated autoloader instead of the one from the package or manually including all required filed
    require_once __DIR__ . '/vendor/autoload.php';
  • Swiftmailer example
    <?php
    
    // Require composer autoloader
    require_once __DIR__ . '/vendor/autoload.php';
    
    // Create the Transport
    $transport = Swift_SmtpTransport::newInstance('relay.odisee.be', 25);
    
    // Create the Mailer using your created Transport
    $mailer = Swift_Mailer::newInstance($transport);
    
    // Create a message
    $message = Swift_Message::newInstance('Lorem Ipsum')
    	->setFrom(array('johndoe@example.org' => 'John Doe'))
    	->setTo(array(
    		'blackhole@bram.us' => 'blackhole'
    	))
    	->setBody(strip_tags(file_get_contents('assets/content.html')))
    	->addPart(file_get_contents('assets/content.html'), 'text/html');
    
    // Send it (or at least try to)
    if(!$mailer->send($message, $errors)) {
    	echo 'Mailer Error: ';
    	print_r($errors);
    } else {
    	echo 'Message sent!';
    }
    
    // EOF

About the Lock File

  • composer.lock has precedence over composer.json.
    • When running composer install it will look for composer.lock.
      If not found, it will fall back to composer.json.
  • Put Differently: It locks the project to those specific versions
    • Successive runs of composer install will use the lock file, even if you've changed composer.json
  • The lock file ensures all devs/servers use the same dependency versions
    • Just clone the source and run composer install

Updating packages

  • Automatic:
    • Run composer require vendor/package newversion on the CLI
  • Manual:
    • Update composer.json
    • Run composer update
  • In both cases a new composer.lock file will be generated.
    • When stuff is verified to still work, commit the lock file into version control

Specifying Package Versions

  • Latest commit: "dev-master"
  • Exact version: "4.2.1"
  • Range: ">=4.1.6,<=5.0"
  • Wildcard: "4.1.*"
    • = equivalent of >=4.1.0,<4.2.0
  • Tilde Operator: "~4.1"
    • = equivalent of >=4.1,<5.0
    • → Recommended; Useful for projects respecting semantic versioning

Composer vs. Version Control

  • A few important notes:
    Don't ever add the vendor/ folder to version control!
    Don't ever change the contents of the vendor/ folder!
    Do add composer.json to version control!
    Do add composer.lock to version control!

Library Close-up: Twig

Twig

Demo time!

Let's take a look at the files in assets/01/examples/with-composer/2.twig/

  • Requirements already defined in composer.json/composer.lock
  • Run composer install
  • Require composer's autoloader.php
  • Use Twig as we did before

Library Close-up: Doctrine DBAL

Doctrine DBAL

Note: Don't confuse Doctrine DBAL with Doctrine ORM (which we won't be using)

Demo time!

Let's take a look at the files in assets/01/examples/with-composer/3.doctrine/

Demo Recap

Don't forget the utf8mb4 charset on the connection!

→ From now on, we'll be using Doctrine DBAL

Want more?

Other libraries worth checking out

Questions?

Sources

ikdoeict.be