00:00:00

PHP

Notes

Who Is Speaking?

Notes

Kévin Gomez

(Web) Developer / OSS enthusiast

Graduated from IUT, Aix-Marseille University and Blaise Pascal University.

Worked since 2009 in various companies. Currently employed by TEA — The Ebook Alternative.

Open-Source enthusiast & contributor:

twitter.com/KPhoen  |  github.com/K-Phoen  |  kevingomez.fr

Notes

Slides originally written by William Durand

Notes

Agenda

Week #1

PHP: Hypertext Preprocessor, The PHP Syntax, Autoloading, The PHP Command Line, Client/Server

Week #2

REST, Leveraging PHP APIs, Dependency Management, Model View Controller

Week #3

Databases

Week #4

Sessions, Authentication, Writing Better Code, Testing, Awesome Projects, Embracing Open Source

Notes

PHP: Hypertext Preprocessor

Notes

History & Numbers

  • Created by Rasmus Lerdorf
  • 4th language on GitHub (August 2015)
  • 6th language in the world (TIOBE January 2016)
  • 1st language for web development
  • Running on +75% of all web servers
  • 20 years old in 2015!

Notes

Notes

Getting Started

Linux

$ sudo apt-get install php5-common libapache2-mod-php5 php5-cli

http://php.net/manual/en/install.unix.debian.php

Notes

Getting Started

Mac OS X

$ curl -s http://php-osx.liip.ch/install.sh | bash -s 7.0

$ curl -s http://php-osx.liip.ch/install.sh | bash -s 5.6

http://php-osx.liip.ch/

Notes

HHVM

HipHop Virtual Machine for PHP, created by Facebook.

HHVM uses a just-in-time compilation approach to achieve superior performance.

http://hhvm.com

Notes

Notes

The PHP Syntax

Notes

Primitive Types

4 scalar types: boolean, integer, float, string;

3 compound types: array, object, callable;

2 special types: resource, null;

And 5 pseudo types: mixed, number, callback, iterable & void (since PHP 7.1).

Note: most of these types have aliases. E.g. double for float.

Read more about the PHP primitive types: http://php.net/manual/en/language.types.intro.php.

Notes

Comparison Operators

// so, this is a PHP variable
$a = 5;

// compare value; return true
var_dump($a == 5);

// compare value (ignore type); return true
var_dump($a == '5');

// compare type/value (integer vs. integer); return true
var_dump($a === 5);

// compare type/value (integer vs. string); return false
var_dump($a === '5');

Read more about comparison operators: http://php.net/manual/en/language.operators.comparison.php.

Timing Attack Safe String Comparison

The hash_equals() function has been added in PHP 5.6 to compare two strings in constant time.

Notes

Operators

$a++; // or: ++$a;

$b--; // or: --$b;

$a && $b;   // AND
$a || $b;   // OR

! $a;       // `true` if $a is not `true`

$a . 'foo'; // concatenation

2 ** 3 = 8 // exponentiation (PHP 5.6+)

But also:

$a  = 'foo';
$a .= 'bar';
// $a => 'foobar'

$b  = 0;
$b += 1;    // $b = 1
$b -= 1;    // $b = 0

$c = 2;
$c **= 3;   // $c = 8

Notes

New Operators in PHP 7.0

Null Coalescing Operator: ??

// Fetches the value of $_GET['user'] and returns 'nobody'
// if it does not exist.
$username = $_GET['user'] ?? 'nobody';

// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

Spaceship Operator: <=>

Returns -1, 0 or 1 when $a is respectively less than, equal to, or greater than $b:

echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

Notes

Classes (1/3)

Simple class definition

class Foo
{
}

Important: No class-level visibility in PHP.

Abstract class definition

abstract class AbstractFoo
{
    abstract public function doSomething();
}

Notes

Classes (2/3)

Creating an instance:

$foo = new Foo();
// $foo = new Foo;

// can also be done with a variable
$class = 'Foo';
$foo   = new $class();

Getting the class name of an instance:

echo get_class($foo);
=> Foo

Useful keyword: instanceof

if ($foo instanceof Foo) {
    // do something
}

http://php.net/manual/en/language.oop5.basic.php

Notes

Classes (3/3)

Anonymous Classes (PHP >= 7.0)

new class {
    public function foo() {
        return 'foo';
    }
}

Anonymous classes behave as traditional classes:

interface Logger {
    public function log($msg);
}

$logger = new class implements Logger {
    public function log($msg) {
        // ...
    }
};

$logger->log('Hello, Anonymous Class');

Notes

Visibility

Keywords

  • public
  • protected
  • private

The Rules

Attribute visibility MUST be set.

Method visibility SHOULD be set.

Methods without any explicit visibility keyword are defined as public.

Class constants visibility CAN be set (as of PHP 7.1).

Notes

Properties

class Foo
{
    const VALUE = 123;
    // PHP 5.6+
    const SENTENCE        = 'The value of VALUE is ' . self::VALUE;
    const ARRAY_OF_VALUES = ['a', 'b'];
    // PHP 7.1+
    private const IMPLEMENTATION_VALUE = 42;

    /** @var int */
    public static $count = 0;

    /** @var Iterator */
    public $iterator;

    /** @var array */
    protected $values = [];

    /** @var string|null */
    private $language = null;
}

Notes

Methods (1/5)

class Foo
{
    public function doSomething()
    {
    }
}

Type Hinting

Works with classes, interfaces, arrays, callable, and Closure. You cannot use scalar types such as int or string with PHP < 7.0:

public function doSomething(Foo $foo);

public function doSomething(Traversable $iterator);

public function doSomething(array $values);

public function doSomething(iterable $values); // accepts array + Traversable

public function doSomething(callable $callback);

public function doSomething(Closure $closure);

Notes

Methods (2/5)

PHP 7 \o/

Scalar Type Declarations

Works with int, float, string, and bool:

function sumOfInts(int $a, int $b) {
    return $a + $b;
}

Return Type Declarations

function sumOfInts(int $a, int $b): int {
    return $a + $b;
}

Notes

Methods (3/5)

PHP 7.1+ \o/

Nullable types

function foo(?string $bar): ?string {
    var_dump($bar);
    return $bar;
}
foo('baz'); // string(3) "baz"
foo(null); // NULL
foo(); // Uncaught Error: Too few arguments to function foo(), 0 passed

Void Return Type

function log(string $message): void {
    fprintf(stderr, '>> '.$message);
}

function log(string $message): void {
    fprintf(stderr, '>> '.$message);
    return true; // Fatal error: A void function must not return a value
}

Notes

Methods (4/5)

The -> operator is used to call methods on objects.

Usage

$foo = new Foo();
$foo->doSomething();

// >= PHP 5.4
(new Foo())->doSomething();

// can also be done with a variable
$method = 'doSomething';
$foo->$method();

$foo->{$method . 'Else'}();
// will call 'doSomethingElse()'; curly braces are required.

Notes

Methods (5/5)

public function doSomething()
{
    // method call
    $this->doSomethingElse();

    // parent method call (inheritance)
    parent::doSomething();

    // accessing a constant
    self::VALUE;

    // accessing a constant from another class
    Bar::ANOTHER_VALUE;

    // accessing an attribute
    return $this->attribute;
}

Notes

Static Keyword

Attributes/Methods can be defined as static:

class Foo
{
    public static $value;

    public static function doThings()
    {
        // accessing a static attribute
        // don't forget the dollar sign!
        self::$value;
    }
}

Warning: the static keyword can also be used to define static variables and for late static bindings. This is different!

Notes

Late Static Bindings

class A
{
    public static function who() { echo __CLASS__; }

    public static function testSelf()
    {
        self::who();
    }

    public static function testStatic()
    {
        static::who();
    }
}

class B extends A
{
    public static function who() { echo __CLASS__; }
}

B::testSelf();
// A

B::testStatic();
// B

Notes

Static Keyword

Usage

$foo = new Foo();

// accessing the attribute from an instance
$foo::$value = 123;

// accessing the attribute directly from the class
echo Foo::$value;
=> 123

Read more: http://php.net/manual/en/language.oop5.static.php.

Notes

Variadic Functions

New operator ... as of PHP 5.6:

function sum(...$numbers)
{
    return array_sum($numbers);
}

echo sum(1, 2);
// 3

Argument Unpacking

$numbers = [ 2, 3 ];
echo sum(1, ...$numbers);
// 6

Notes

Variadic Functions & type hints

Works with type hints too!

function sum(int ...$numbers): int
{
    return array_sum($numbers);
}

echo sum(1, 2);
// 3

Argument Unpacking

$numbers = [ 2, 3 ];
echo sum(1, ...$numbers);
// 6

Notes

Interfaces

Simple interface definition

interface Fooable
{
    const VALUE = 123;

    // it's always public anyway
    public function doSomething();
}

Inheritance

// Interface may extend several other interfaces.
// This is not possible with classes though!
interface MyTraversable extends Traversable, Countable
{
}

Usage

// a class may implement several interfaces, but may extend only one class!
class Foo implements Fooable, MyTraversable {}

Notes

Namespaces (1/2)

Namespaces prevent naming collisions with identifiers such as function, class, and interface names:

namespace Vendor\Model;
// ...

Or:

namespace MyNamespace {
    // ...
}

Notes

Namespaces (2/2)

Classes, functions, and constants have to be imported with the use statement:

namespace My\Namespace;

// Pre PHP 7 code
use some\namespace\ClassA;
use some\namespace\ClassB;

use function some\namespace\fn_a;
use function some\namespace\fn_b;

// PHP 7+ code
use some\namespace\{ClassA, ClassB};

use function some\namespace\{fn_a, fn_b};

class MyClass
{
    public function __construct(ClassA $a, ClassB $b) {
        // ...
    }
}

Notes

The class Keyword

Since PHP 5.5.0, class name resolution is possible via ::class.

namespace My\Namespace;

class ClassName
{
}

Assuming the class definition above, you can get the Fully Qualified Class Name (FQCN) by doing:

echo ClassName::class;
// My\namespace\ClassName

Read more about the class keyword: http://php.net/manual/en/language.oop5.basic.php.

Notes

Traits

Horizontal Inheritance FTW!

trait Hello                         trait World
{                                   {
    public function sayHello()          public function sayWorld()
    {                                    {
        echo 'Hello ';                       echo 'World';
    }                                    }
}                                   }

class MyHelloWorld
{
    use Hello, World;
}

$obj = new MyHelloWorld();
$obj->sayHello();
$obj->sayWorld();

Read more about traits: http://php.net/manual/en/language.oop5.traits.php.

Notes

Anonymous Functions

An anonymous function, also known as lambda function, is a function defined, and possibly called, without being bound to an identifier.

$greet = function ($name) {
    printf("Hello %s\n", $name);
};

$greet('World');
=> Hello World

Read more about anonymous functions: http://php.net/manual/en/functions.anonymous.php.

Notes

Closures

A closure is an anonymous function that owns a context.

$fibonacci = function ($n) use (&$fibonacci) {
    if (0 === $n || 1 === $n) {
        return $n;
    }

    return $fibonacci($n - 1) + $fibonacci($n - 2);
};

echo (int) $fibonacci(6);
=> 8

Read more about closures: http://php.net/manual/en/class.closure.php.

Notes

Magic Methods

Starts with __.

Two useful methods:

__construct() { /* ... */ }

and:

__toString() { /* ... */ }

Other methods are not really useful but it's worth knowing them (__get(), __set(), __invoke(), …).

Read more about magic methods: http://php.net/manual/en/language.oop5.magic.php.

Notes

Generators

A generator function looks just like a normal function, except that instead of returning a value, a generator yields as many values as it needs to.

The heart of a generator function is the yield keyword.

Read more about generators:

Notes

Errors in PHP 7

No more Way less Fatal Errors \o/

Many fatal and recoverable fatal errors have been converted to exceptions inheriting from the new Error class, which itself implements the Throwable interface, i.e. the new base interface all exceptions inherit.

https://secure.php.net/manual/en/language.errors.php7.php

Notes

PHP Autoloading

Notes

Why Is It Necessary?

PHP won't magically load your classes by itself.

You have to manually include the class declaration:

// src/Model/Octopus.php
class Octopus
{
    public function scream()
    {
        echo "o o O O  ° °";
    }
}

If you want to create a new Octopus, you will write the following code:

// src/Application.php
$paul = new Octopus();
$paul->scream();

As the class declaration isn't included, PHP raises a Fatal Error:

Fatal error: Class 'Octopus' not found in []/src/Application.php

Notes

The require() Way

Include the class definition before instantiating it:

require __DIR__ . '/Model/Octopus.php';

$paul = new Octopus();
$paul->scream();

It works!

But, what happens when the class is included again, somewhere else?

// somewhere further in your application
require __DIR__ . '/Model/Octopus.php';

class Squid extends Octopus
{
}

PHP raises a Fatal Error:

Fatal error: Cannot redeclare class Octopus in /path/to/file.php

Notes

The require_once() Way

The require_once() function is identical to require() except that PHP will check whether the file has already been included:

require_once __DIR__ . '/Model/Octopus.php';

$paul = new Octopus();
$paul->scream();

And somewhere else:

// somewhere further in your application
require_once __DIR__ . '/Model/Octopus.php';

class Squid extends Octopus
{
}

It just works!

Notes

Working With Multiple Files

Multiple require_once() can turn into a nightmare when you deal with more than a few files:

/**
 * lib/Model/Location.php
 */
require_once __DIR__ . '/../../common.php';
require_once DOCROOT . '/lib/Model/ModelRepresentation.php';
require_once DOCROOT . '/lib/Model/User.php';
require_once DOCROOT . '/lib/Validator/Email.php';
require_once DOCROOT . '/lib/Validator/Username.php';
require_once DOCROOT . '/lib/Validator/Url.php';

class Location implements ModelRepresentation
{
    /* ... */
}

Notes

Rethinking The Way You Load Classes

PHP 5.2 and upper provides a usable autoloading API with performances close to the use of require_once() thanks to the following functions:

__autoload()

Main autoload callback.

spl_autoload_register()

Register a new autoload callback.

spl_autoload_unregister()

Unregister an autoload callback.

spl_autoload_functions()

List all autoload methods.

Notes

Examples

__autoload()

function __autoload($className)
{
    require_once __DIR__ . DIRECTORY_SEPARATOR . $className . '.php';
}

spl_autoload_register()

function my_autoload($className)
{
    require_once __DIR__ . DIRECTORY_SEPARATOR . $className . '.php';
}

spl_autoload_register('my_autoload');

spl_autoload_unregister()

spl_autoload_unregister('my_autoload');

Notes

Under The Hood

new Foo();

The new algorithm in pseudo code:

1. Does the 'Foo' class exist?
    => Yes
        Go on

    => No
         Do you have registered autoload functions?
            => Yes
                Call each function with 'Foo' as parameter
                until the class gets included

            => No
                Is there a `__autoload()` method?
                    => Yes
                        Call `__autoload('Foo')`

2. Does the 'Foo' class exist?
    => Yes
        Continue
    => No
        Fatal Error

Notes

PSR-0 & PSR-4

PSR-0

PSR-0 describes a set of rules related to namespaces for autoloader interoperability:

\Zend\Mail\Message
// => /path/to/project/lib/vendor/Zend/Mail/Message.php

Zend_Mail_Message
// => /path/to/project/lib/vendor/Zend/Mail/Message.php

Important: as of 2014-10-21 PSR-0 has been marked as deprecated.

PSR-4

Like PSR-0, but better:

  • more concise folder structure;
  • remove the remnants of PSR-0 (e.g. PEAR support).

Notes

The PHP Command Line

Notes

The PHP Command Line (1/3)

PHP is an interpreted language, no need for a compiler.

You can try PHP using the command line:

$ php -r 'echo "Hello, World\n"'
Hello, World

Help available by running: php -h

PHP also provides an interactive shell:

$ php -a
Interactive Shell

php > echo "Hello, World\n";
Hello, World

The command line is really useful, read more about command line options: http://php.net/manual/en/features.commandline.options.php.

Notes

The PHP Command Line (2/3)

Pro-tip: PHP built-in interactice shell kinda sucks. Use Psysh.

Installation:

$ wget https://git.io/psysh
$ chmod +x psysh
$ ./psysh
  • auto-completion
  • syntax highlighting
  • documentation access
  • debugging

Notes

The PHP Command Line (3/3)

Your new best friend is the linter:

$ php -l my/script.php
No syntax errors detected in my/script.php

A linter is a program that looks for problems in your code (syntax errors for instance).

Embedded web server:

$ php -S localhost:8000

Learn more about the built-in, command line web server: http://php.net/manual/en/features.commandline.webserver.php.

Notes

Writing a CLI program

#!/usr/bin/env php
<?php

if (2 !== $argc) {
    echo "Usage: php $argv[0] [name]\n";
    exit(1);
}

$name = $argv[1];
echo "Hello, $name!\n";

Run the script:

$ ./hello.php
Usage: ./hello.php [name]

$ php hello.php
Usage: php hello.php [name]

$ php hello.php World
Hello, World!

Notes

Client/Server

Notes

Client/Server Basics

A typical client/server request follows this pattern:

  1. Client: Hello server, give me the resource at URI;
  2. Server: Here is the resource at URI:

    Content

For HTTP, a typical client is a web browser, and a server is a web server.

Notes

Unified Resource Identifier (URI)

In A Nutshell

  • URIs identify resources;
  • URIs are format independent;
  • URI "file extensions" != RESTful.

Resources

  • /bananas/joe: URI for banana "Joe"
  • /bananas/henry: URI for banana "Henry"

Collections

  • /bananas: collection of all available bananas

Notes

HTTP Request

Request is made of:

  • A Unique Resource Identifier (URI);
  • An HTTP verb (method) describing the action;
  • Some headers describing requirements;
  • A request body to send data.

Here is an example:

GET /my/simple/uri?with-query-string HTTP/1.1
Host: example.org
Content-Type: text/plain; charset=utf-8
Content-Length: 17

This is a content

Notes

HTTP Verbs

An HTTP verb is an action to perform on a resource located at a given URI:

  • GET: retrieve a resource or a collection of resources;
  • POST: create a new resource;
  • PUT: update an existing resource or create a new resource at a given URI;
  • DELETE: delete a given resource;
  • PATCH: partial update of a given resource.

Important: this list is not exhaustive.

Notes

HTTP Response

Response is made of:

  • The response's status code;
  • Some headers to describe the content;
  • The content of the response;

Here is an example:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 76

<!DOCTYPE HTML>
<html>
    <head>
    </head>
    <body>
        <h1>Hello world !</h1>
    </body>
</html>

Notes

Status Codes (1/2)

1xx Informational

2xx Successful

  • 200 OK
  • 201 Created
  • 204 No Content

3xx Redirections

  • 301 Moved Permanently
  • 302 Found
  • 304 Not Modified

httpstatus.es

Notes

Status Codes (2/2)

4xx Client Error

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 405 Method Not Allowed
  • 406 Not Acceptable
  • 409 Conflict
  • 415 Unsupported Media Type
  • 451 Unavailable For Legal Reasons

5xx Server Error

  • 500 Internal Server Error

Notes

HTTP Parameters (1/2)

There are two types of parameters, query string and request body.

If the request follows the URL Form Encoded format, you can access parameters through global variables:

  • query string: $_GET;
  • request body: $_POST;
  • All parameters are available in the $_REQUEST global variable.

You can always use the following, but you need to parse them by yourself:

  • query string: $_SERVER['QUERY_STRING'];
  • request body: $HTTP_RAW_POST_DATA (deprecated, do not use).

Note: Don't use $_REQUEST, as there is a collision risk!

Notes

HTTP Parameters (2/2)

GET /my/simple/uri?a=1&id=2&c=42 HTTP/1.1
Host: example.org
Content-Type: text/plain; charset=utf-8
Content-Length: 14

b=3&city=paris

Will result in:

$_GET     = [ "a" => 1, "id" => 2 ];

$_POST    = [ "b" => 3, "city" => 'paris' ];

$_REQUEST = [ "a" => 1, "id" => 2, "b" => 3, "city" => 'paris' ];

$_SERVER['QUERY_STRING'] = "a=1&id=2";

$HTTP_RAW_POST_DATA      = "b=3&city=paris";

Important: never trust user input, never!

Notes

API

Notes

Defining APIs

« A set of subroutine definitions, protocols, and tools for building application software » — Wikipedia

« Sets of requirements that govern how one application can talk to another » — ReadWrite

« With APIs, applications talk to each other without any user knowledge or intervention » — HowStuffWorks

« A contract provided by one piece of computer software to another » — Quora

Notes

Defining APIs

  • An API allows different computer systems to interact and share data together
  • It's a way for a developer or a company to say "hey, here is my data and there are the services you can use, and how."

Notes

REST

Notes

REpresentational State Transfer

REST is the underlying architectural principle of the web, formalized as a set of constraints, described in Roy Fielding's dissertation.

An API (i.e. a web service) that adheres to the principles of REST does not require the client to know anything about the structure of this API. Rather, the server needs to provide whatever information the client needs to interact with the service.

The key abstraction of information in REST is a resource. Any information that can be named can be a resource, and is identified by a Unified Resource Identifier (URI).

It heavily relies on the HTTP protocol: RFC 2616.

Notes

Richardson Maturity Model



Notes

Level 0 - The Swamp of POX

In A Nutshell

  • HTTP as a tunneling mechanism;
  • RPC style system (SOAP, XML-RPC).



Notes

Level 1 - Resources

In A Nutshell

  • Individual resources (URIs);
  • Notion of object identity.



Notes

Level 2 - HTTP Verbs

In A Nutshell

  • Client uses specific HTTP verbs;
  • Server uses HTTP status codes.



Notes

Level 3 - Hypermedia Controls

In A Nutshell

  • Service discovery via link relations
  • Hypermedia formats


Notes

Level 3 = Content Negotiation + HATEOAS

Notes

Media Types

In A Nutshell

  • Identifies a representation format;
  • Custom types use application/vnd.[XYZ];
  • Used inside the Accept / Content-Type headers.
Header Description
`Content-Type`HTTP message format
`Accept`HTTP response format preference

Hyper Media Types

Hyper Media Types are MIME media types that contain native hyper-linking semantics that induce application flow: application/hal+json, application/collection+json, etc.

Notes

Content Type Negotiation

Content Type Negotiation is the principle of finding appropriate response formats based on client requirements.

No standardized algorithm available, even if the Apache mod_negotiation algorithm is documented. This also covers encoding (Accept-Encoding) and language (Accept-Language) negotiation.

Accept: application/json, application/xml;q=0.9, text/html;q=0.8,
    text/*;q=0.7, */*;q=0.5
Priority Mime Type
`q=1.0``application/json`
`q=0.9``application/xml`
`q=0.8``text/html`
`q=0.7``text/*` (ie. any text)
`q=0.5``*/*` (ie. any media type)

Notes

HATEOAS

HATEOAS stands for Hypermedia As The Engine Of Application State. It means that hypertext should be used to find your way through the API.

It is all about state transitions. Your application is just a big state machine. There should be a single endpoint for the resource, and all of the other actions you would need to undertake should be able to be discovered by inspecting that resource.

<?xml version="1.0" encoding="UTF-8"?>
<collection page="1" limit="10" pages="1">
    <user id="123"></user>
    <user id="456"></user>

    <link rel="self" href="/api/users?page=1&amp;limit=10" />
    <link rel="first" href="/api/users?page=1&amp;limit=10" />
    <link rel="last" href="/api/users?page=1&amp;limit=10" />
</collection>

Must read: Haters gonna HATEOAS.

Notes

Idempotent vs Safe methods

« Idempotence is the property of certain operations […], that can be applied multiple times without changing the result beyond the initial application » — Wikipedia

Safe methods are HTTP methods that do not modify resources

Notes

Idempotent vs Safe methods

Method Idempotent Safe
GET Yes Yes
HEAD Yes Yes
POST No No
PUT Yes No
DELETE Yes No
PATCH No (but really, it depends) No

Notes

Leveraging PHP APIs

Notes

Built-in Interfaces

ArrayAccess

Access properties as an array:

$tom = new MyObject();
$tom['name'] = 'Tom';

Serializable, JsonSerializable

Allow the use of serialize() and unserialize(). Objects implementing JsonSerializable can customize their JSON representation when encoded with json_encode().

Traversable

Allow the use of foreach.

Read more: http://php.net/manual/en/reserved.interfaces.php.

Notes

The Reflection API (1/2)

Enable code introspection:

/** A comment */
class MyClass
{
    public function hello() { printf("Hello %s", $this->getName()); }

    protected function getName() { return 'foo'; }
}

$reflClass = new ReflectionClass('MyClass');

// access comments
var_dump($reflClass->getDocComment());
// string(16) "/** A comment */"

// get all methods
$reflClass->getMethods();

// get all public methods
$reflClass->getMethods(ReflectionMethod::IS_PUBLIC);

Notes

The Reflection API (2/2)

It is even possible to invoke private methods!

class MyClass
{
    public function hello() { printf("Hello %s", $this->getName()); }

    private function getName() { return 'foo'; }
}

$reflClass = new ReflectionClass('MyClass');

// access private method
$method = $reflClass->getMethod('getName');
$method->setAccessible(true);

$method->invoke(new MyClass());
// 'foo'

Read more: http://php.net/manual/en/book.reflection.php.

Notes

The Standard PHP Library (SPL)

Provides a collection of classes and interfaces:

Datastructures

SplStack, SplQueue, SplObjectStorage, etc.

Named Exceptions

LogicException, InvalidArgumentException, OutOfRangeException, RuntimeException, etc.

SPL Functions

class_parents(), spl_autoload_register(), spl_autoload_unregister(), etc.

Read more about the SPL: http://php.net/manual/en/book.spl.php.

Notes

Observer Pattern (1/2)

The SplObserver interface is used alongside SplSubject to implement the Observer Design Pattern.

class Subject implements SplSubject
{
    /* ... */
}

class Observer implements SplObserver
{
    /* ... */
}

$subject = new Subject();

$observer1 = new Observer();

$subject->attach($observer1);

$subject->notify();

Read more: http://php.net/manual/en/class.splobserver.php.

Notes

Observer Pattern (2/2)

Those interfaces are never used as there is only one default channel for the notify() method.

Symfony2 EventDispatcher component to the rescue!

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;

$dispatcher = new EventDispatcher();

$dispatcher->addListener('event_name', function (Event $event) {
    // ...
});

$dispatcher->dispatch('event_name');

Read more: http://symfony.com/doc/master/components/event_dispatcher/.

Notes

Exceptions (1/3)

try-catch block with multiple catch statements:

try {
    throw new ResourceNotFound();
} catch (RuntimeException $e) {
    // do something
} catch (Exception $e) {
    // do something else
}

Create your own exceptions:

class SomethingWentWrong extends RuntimeException
{
}

class ErrorWhileDoingSomething extends Exception implements ExceptionInterface
{
}

Name your exceptions!

Notes

Exceptions (2/3)

Since PHP 7.1, a single catch block can catch multiple exceptions!

try {
    // ...
} catch (RuntimeException | LogicException $e) {
    // do something
} catch (Exception $e) {
    // do something else
}

Notes

Exceptions (3/3)

try-catch blocks also supports a finally block for code that should be run regardless of whether an exception has been thrown or not:

try {
    // ..
} catch (Exception $e) {
    // do something
} finally {
    // the code here will always be executed
}

Notes

"Funny" gotcha

What does this code print?

function foo(): string
{
    try {
        return 'bar';
    } finally {
        return 'baz';
    }
}
var_dump(foo()); // ?

Notes

Password Hashing

The password hashing API provides an easy to use wrapper around crypt() to make it easy to create and manage passwords in a secure manner, since PHP 5.5.0.

password_hash() and password_verify() are your new friends!

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    // Correct Password
} else {
    // Wrong password
}

Read more about the Password Hashing API: http://php.net/manual/en/book.password.php.

A userland implementation exists for PHP >= 5.3.7: password_compat.

Notes

PHP Archive (PHAR)

The phar extension provides a way to put entire PHP applications into a single file called a "phar" (PHP Archive) for easy distribution and installation.

But, the API is hard to use.

Solution? Box, a command line for simplifying the PHAR creation process.

Read more about PHAR:

Notes

Dependency Management

Notes

Composer

There are a ton of PHP libraries, frameworks, and components to choose from. Most of them have different versions, and don't always work well together.

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.

A lot of awesome PHP libraries are compatible with Composer and listed on Packagist, the official repository for Composer-compatible PHP libraries.

$ curl -sS https://getcomposer.org/installer | php

This will download composer.phar (a PHP binary archive).

http://getcomposer.org/doc/00-intro.md

Notes

composer install

Create a composer.json file in your project's root directory:

{
    "require": {
        "willdurand/geocoder": "~2.0"
    }
}

You can also require a library by using the require command:

$ php composer.phar require willdurand/geocoder

Run the following command to download and install the project dependencies into a vendor directory:

$ php composer.phar install

Composer Version Constraints

Notes

Composer Autoloader

Composer automatically generates a PSR-4 compliant and optimized autoloader for your entire application. Thanks to Composer, you don't have to take care about how to autoload classes/functions anymore.

Require the generated autoloader in your project as follows:

<?php

require 'vendor/autoload.php';

// your PHP code

Must read: Composer Primer.

Notes

Example

Instead of writing a console application by hand, let's use an existing library: the Symfony2 Console component:

{
    "require": {
        "symfony/console": "~2.4"
    }
}

The structure of your application should look like:

console-app
├── app
│   └── console
├── composer.json
├── src
├── tests
└── vendor

Notes

The Symfony2 Console Component

The easiest way to write strong console applications:

  • Create a set of commands;
  • Add them to a console application.

Your Commands should extend the Command class:

class GreetCommand extends Command
{
    protected function configure()
    {
        // configure the name, arguments, options, etc.
    }

    protected function execute(InputInterface $in, OutputInterface $out) {
        // do greet
    }
}

Notes

A Basic Command (1/2)

Configuration

$this
    ->setName('demo:greet')
    ->addArgument(
        'name',
        InputArgument::OPTIONAL,
        'Who do you want to greet?'
    );

Execution

if (null === $name = $input->getArgument('name')) {
    $name = 'World';
}

$output->writeln('Hello, ' . $name);

Notes

A Basic Command (2/2)

The Console Application

#!/usr/bin/env php
# app/console
<?php

// require the Composer autoloader
require __DIR__ . '/../vendor/autoload.php';

$application = new Application();
$application->add(new GreetCommand());
$application->run();

Usage

$ app/console demo:greet
Hello, World
$ app/console demo:greet William
Hello, William

Read more: http://symfony.com/doc/current/components/console/.

Notes

Model View Controller

Notes

MVC Overview

Typical client request process in MVC architecture:

Notes

The Model

Model is the layer in charge of data interaction.

All data related business logic is embedded here. Using it should not require to understand internals.

Examples:

  • Manipulate database records;
  • Communicate with search engine;
  • API calls;
  • etc.

More on this next week!

More on this in a few minutes!

Notes

The View

PHP is a templating language per se.

Never, ever, ever mix HTML and PHP codes or kittens will die: you have to separate the presentation from the business logic.

class PhpTemplateEngine implements TemplateEngine
{
    private $templateDir;

    public function __construct($templateDir)
    {
        $this->templateDir = $templateDir;
    }

    public function render($template, array $parameters = [])
    {
        extract($parameters);

        ob_start();
        include $this->templateDir . DIRECTORY_SEPARATOR . $template;

        return ob_get_clean();
    }
}

Notes

The View

Template

<!-- my_template.html -->
<p>Hello, <?php echo $name; ?>!</p>

Even better with PHP 5.4+:

<p>Hello, <?= $name ?>!</p>

Usage

$engine = new PhpTemplateEngine('/path/to/templates');

echo $engine->render('my_template.html', [
    'name' => 'World',
]);
=> <p>Hello, World!</p>

Notes

The View

Twig is a modern template engine for PHP. It takes care of escaping for you and much much more! Read more: http://twig.sensiolabs.org/.

Template

{# my_template.html #}
<p>Hello, {{ name }}!</p>

Usage

$loader = new Twig_Loader_Filesystem('/path/to/templates');
$engine = new Twig_Environment($loader, [
    'cache' => '/path/to/compilation_cache',
]);

echo $engine->render('my_template.html', [
    'name' => 'World',
]);
=> <p>Hello, World!</p>

Notes

The Controller

Glue between the Model and the View layers.

It should not contain any business logic.

class BananaController
{
    public function __construct(
        BananaRepository $repository,
        TemplateEngine $engine
    ) {
        $this->repository = $repository;
        $this->engine = $engine;
    }

    public function listAction()
    {
        $bananas = $this->repository->findAll();

        return $this->engine->render('list.html', [
            'bananas' => $bananas,
        ]);
    }
}

Notes

Routing

Routing is the process of binding URIs to controllers.

Folder organization

The simplest kind of routing, but also the hardest one to maintain:

web/
├ trees/
│ └ pineapple.php
└ tree.php

Centralized Declaration

Modern frameworks provide a routing component such as the Symfony2 Routing component allowing to define routes in a centralized place, and easing URI generation.

This require a single entry point: the Front Controller.

Notes

Front Controller Pattern

A controller that handles all requests for a web application:

This controller dispatches the request to the specialized controllers.

It is usually tied to URL rewriting.

Notes

Databases

Notes

Agenda

  • Data Access Layer
  • Database Design Patterns
  • Object Relational Mapping
  • Existing Components
  • A Note About Domain-Driven Design

Notes

Quick note

In our context, a database is seen as a server hosting:

  • a set of records;
  • organised through tables or collections;
  • grouped by databases.

Notes

Data Access Layer

Notes

Data Access Layer / Data Source Name

A Data Access Layer (DAL) is a standard API to manipulate data, no matter which database server is used. A Data Source Name (DSN) can be used to determine which database vendor you are using.

PHP Data Object (PDO)

A DSN in PHP looks like: <database>:host=<host>;dbname=<dbname> where:

  • <database> can be: mysql, sqlite, pgsql, etc;
  • <host> is the IP address of the database server (e.g. localhost);
  • <dbname> is your database name.

http://www.php.net/manual/en/intro.pdo.php

Notes

Data Access Layer

PDO usage

$dsn = 'mysql:host=localhost;dbname=test';
// or for postgresql → $dsn = 'pgsql:host=localhost;dbname=test';
// or for oracle → $dsn = 'oci:host=localhost;dbname=test';

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];

$con = new PDO($dsn, $user, $password, $options);

// Prepared statement
$stmt = $con->prepare($query);
$stmt->execute();

Advantage

An abstraction that works with any database*

* you still need to install the drivers for your database

Notes

Writing queries

/* @var PDOStatement $stmt */
$stmt = $conn->prepare('SELECT name FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);

/* @var array $user */
while ($user = $stmt->fetch()) {
    echo $user['name'] . "\n";
}

Advantage

  • safe by default: no SQL injection possible if you only use prepared statements and variable placeholders

Other usage examples can be found here: https://phpdelusions.net/pdo

Notes

Making our live easier

Let's introduce the following Connection class:

class Connection extends PDO
{
}

Usage

$con = new Connection($dsn, $user, $password);

Notes

Making our live easier

And add an "utility" method:

class Connection extends PDO
{
    /**
     * @param string $query
     * @param array  $parameters
     *
     * @return bool Returns `true` on success, `false` otherwise
     */
    public function executeQuery($query, array $parameters = [])
    {
        $stmt = $this->prepare($query);

        return $stmt->execute($parameters);
    }
}

Usage

$conn->executeQuery('INSERT INTO users (name, email) VALUES (:name, :email)', [
    ':name' => $name,
    ':email' => $email,
])

Notes

The problem(s)

  • How do we manipulate this records from within our application? Do we just pass around the Connection object?
  • How do we handle database failures?
  • How do we write tests?

→ We need a way to represent in our code the interactions between our application and the database.

Notes

Design Patterns to the rescue

Database-related Patterns:

  • Row Data Gateway
  • Table Data Gateway
  • Active Record
  • Data Mapper
  • Identity Map
  • etc.

Definitions and figures are part of the Catalog of Patterns of Enterprise Application Architecture created by Martin Fowler.
Don't forget his name! Read his books and blog!

Notes

Row Data Gateway

Notes

Row Data Gateway

An object that acts as a Gateway to a single record (row) in a database. There is one instance per row.

// This is the implementation of `BananaGateway`
class Banana
{
    private $id;

    private $name;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

Notes

Row Data Gateway

Usage

$con = new Connection('...');

$banana = new Banana();
$banana->setName('Super Banana');

// Save the banana
$banana->insert($con);

// Update it
$banana->setName('New name for my banana');
$banana->update($con);

// Delete it
$banana->delete($con);

Notes

Row Data Gateway

Under the hood

class Banana
{
    // …

    public function insert(Connection $con)
    {
        // Prepared statement
        $stmt = $this->con->prepare('INSERT INTO bananas VALUES (:name)');

        $stmt->execute([':name' => $this->name]);

        // Set the id for this banana
        //
        // It becomes easy to know whether the banana is new or not,
        // you just need to check if id is defined.
        $this->id = $this->con->lastInsertId();
    }
}

Notes

Row Data Gateway — Summary

  • One row = One object
  • Each column is mapped to a class attribute
  • Rows are accessed through finders
  • Creation, update and deletion are handled by the row gateway
  • NO domain logic in the objects

Notes

Table Data Gateway

Notes

Table Data Gateway

An object that acts as a Gateway to a database table. One instance handles all the rows in the table.

It's a Data Access Object.

$table = new BananaGateway(new Connection('...'));

// Insert a new record
$id = $table->insert('My favorite banana');

// Update it
$table->update($id, 'THE banana');

// Delete it
$table->delete($id);

CRUD

A DAO implements the well-known Create Read Update Delete methods.

Read should not be a single method: Finders to the rescue!

Notes

Table Data Gateway

Implementation

class BananaGateway
{
    private $con;

    public function __construct(Connection $con)
    {
        $this->con = $con;
    }

    public function insert($name) {}

    public function update($id, $name) {}

    public function delete($id);
}

Notes

Table Data Gateway

The insert method

/**
 * @param string $name The name of the banana you want to create
 *
 * @return int The id of the banana
 */
public function insert($name)
{
    // Prepared statement
    $stmt = $this->con->prepare('INSERT INTO bananas VALUES (:name)');

    $stmt->execute([':name' => $name]);

    return $this->con->lastInsertId();
}

Notes

Table Data Gateway

The update method

/**
 * @param int    $id   The id of the banana to update
 * @param string $name The new name of the banana
 *
 * @return bool Returns `true` on success, `false` otherwise
 */
public function update($id, $name)
{
    $stmt = $this->con->prepare(<<<SQL
UPDATE bananas
SET name = :name
WHERE id = :id
SQL
    );

    return $stmt->execute([':id' => $id, ':name' => $name]);
}

Notes

Table Data Gateway

The delete method

/**
 * @param int $id The id of the banana to delete
 *
 * @return bool Returns `true` on success, `false` otherwise
 */
public function delete($id)
{
    $stmt = $this->con->prepare('DELETE FROM bananas WHERE id = :id');

    return $stmt->execute([':id' => $id]);
}

Notes

Table Data Gateway

Finders

// Retrieve all bananas
$bananas = $table->findAll();

// Find bananas by name matching 'THE %'
$bananas = $table->findByName('THE %');

// Retrieve a given banana using its id
$banana = $table->find(123);

// Find one banana by name matching 'THE %'
$banana = $table->findOneByName('THE %');

Use the __call() magic method to create magic finders: http://www.php.net/manual/en/language.oop5.overloading.php#object.call.

Notes

Table Data Gateway — Summary

  • One table = One gateway class
  • CRUD operations are handled by the gateway
  • Finder methods directly in the gateway
  • NO domain logic in the objects

Notes

Active Record

Notes

Active Record

An object that wraps a row in a database table, encapsulates the database access, and adds domain logic on that data.

Active Record = Row Data Gateway + Domain Logic

$con = new Connection('...');

$banana = new Banana();
$banana->setName('Another banana');
$banana->save($con);

// Call a method that is part of the domain logic
// What can a banana do anyway?
$banana->grow();

// Smart `save()` method
// use `isNew()` under the hood
$banana->save($con);

Notes

Active Record

class Banana
{
    private $height = 1;

    public function grow()
    {
        $this->height++;
    }

    public function save(Connection $con)
    {
        if ($this->isNew()) {
            // issue an INSERT query
        } else {
            // issue an UPDATE query
        }
    }

    public function isNew()
    {
        return $this->id === null;
    }
}

Notes

Data Mapper

Notes

Data Mapper

A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself.

Sort of "Man in the Middle".

class BananaMapper
{
    private $con;

    public function __construct(Connection $con)
    {
        $this->con = $con;
    }

    public function persist(Banana $banana)
    {
        // code to save the banana
    }

    public function remove(Banana $banana)
    {
        // code to delete the banana
    }
}

Notes

Data Mapper

Usage

$banana = new Banana();
$banana->setName('Fantastic Banana');

$con    = new Connection('...');
$mapper = new BananaMapper($con);

Persist = Save or Update

$mapper->persist($banana);

Remove

$mapper->remove($banana);

Notes

Identity Map

Notes

Identity Map

Ensures that each object gets loaded only once by keeping every loaded object in a map. Looks up objects using the map when referring to them.

class Finder
{
    private $identityMap = [];

    public function find($id)
    {
        if (!isset($this->identityMap[$id])) {
            // fetch the object for the given id
            $this->identityMap[$id] = ...;
        }

        return $this->identityMap[$id];
    }
}

Notes

Object Relational Mapping

Notes

Object Relational Mapping (1/4)

Introduces the notion of relations between objects:

  • One-To-One;
  • One-To-Many;
  • Many-To-Many.

An ORM is often considered as a tool that implements some design patterns seen above, and that eases relationships between objects.

Notes

Object Relational Mapping (2/4)

One-To-One (1-1)

Code Snippet

$profile = $banana->getProfile();

Notes

Object Relational Mapping (3/4)

One-To-Many (1-N)

Code Snippet

$bananas = $bananaTree->getBananas();

Notes

Object Relational Mapping (4/4)

Many-To-Many (N-N)

Code Snippet

$roles = [];
foreach ($banana->getBananaRoles() as $bananaRole) {
    $roles[] = $bananaRole->getRole();
}

// Or, better:
$roles = $banana->getRoles();

Notes

Existing Components

Propel ORM

An ORM that implements the Table Data Gateway and Row Data Gateway patterns, often seen as an Active Record approach.

Documentation: www.propelorm.org.

Doctrine2 ORM

An ORM that implements the Data Mapper pattern.

Documentation: www.doctrine-project.org.

Notes

A Note About
Domain-Driven Design

Notes

Entities

An object defined primarily by its identity is called an entity:

class Customer
{
    private $id;

    private $name;

    public function __construct($id, Name $name)
    {
        $this->id   = $id;
        $this->name = $name;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }
}

Notes

Value Objects

An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object:

class Name
{
    private $firstName;

    private $lastName;

    public function __construct($firstName, $lastName)
    {
        $this->firstName = $firstName;
        $this->lastName  = $lastName;
    }

    public function getFirstName()
    {
        return $this->firstName;
    }

    public function getLastName()
    {
        return $this->lastName;
    }
}

Notes

The Repository Pattern

Notes

The Repository Pattern

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.

interface CustomerRepository
{
    /**
     * @return Customer
     */
    public function find($customerId);

    /**
     * @return Customer[]
     */
    public function findAll();

    public function add(Customer $user);

    public function remove(Customer $user);
}

Notes

The Repository Pattern

Client objects construct query specifications declaratively and submit them to Repository for satisfaction.

Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes.

Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer.

Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

Notes

The Specification Pattern

Notes

The Specification Pattern

The Specification pattern is a way to model business rules as individual objects. The idea is that a question about an object, is answered by a isSatisfiedBy() method:

interface CustomerSpecification
{
    /**
     * @return boolean
     */
    public function isSatisfiedBy(Customer $customer);
}

class CustomerIsPremium implements CustomerSpecification
{
    /**
     * {@inheritDoc}
     */
    public function isSatisfiedBy(Customer $customer)
    {
        // figure out if the customer is indeed premium,
        // and return true or false.
    }
}

Notes

Repository ♥ Specification

A findSatisfying() method can be added to the CustomerRepository:

interface CustomerRepository
{
    ...

    /**
     * @return Customer[]
     */
    public function findSatisfying(CustomerSpecification $specification);
}

Usage

$specification = new CustomerIsPremium();
$customers     = $repository->findSatisfying($specification);

Notes

Combine Them!

class OrSpecification implements CustomerSpecification
{
    public function __construct(
        CustomerSpecification $s1,
        CustomerSpecification $s2
    ) {
        $this->s1 = $s1;
        $this->s2 = $s2;
    }

    public function isSatisfiedBy(Customer $c)
    {
        return $this->s1->isSatisfiedBy($c) || $this->s2->isSatisfiedBy($c);
    }
}

class AndSpecification implements CustomerSpecification
{
    ...

    public function isSatisfiedBy(Customer $c)
    {
        return $this->s1->isSatisfiedBy($c) && $this->s2->isSatisfiedBy($c);
    }
}

Notes

Combine Them!

class NotSpecification implements CustomerSpecification
{
    public function __construct(CustomerSpecification $s)
    {
        $this->s = $s;
    }

    public function isSatisfiedBy(Customer $c)
    {
        return !$this->s->isSatisfiedBy($c);
    }
}

Usage

// Find customers who have ordered exactly three times,
// but who are not premium customers (yet?)
$specification = new AndSpecification(
    new CustomerHasOrderedThreeTimes(),
    new NotSpecification(
        new CustomerIsPremium()
    )
);

$customers = $repository->findSatisfying($specification);

Notes

Specification For Business Rules

Reuse your specifications in your business layer:

class AwesomeOfferSender
{
    private $specification;

    public function __construct(CustomerIsPremium $specification)
    {
        $this->specification = $specification;
    }

    public function sendOffersTo(Customer $customer)
    {
        if ($this->specification->isSatisfiedBy($customer)) {
            // send offers
        }
    }
}

Notes

Checkout: RulerZ

Notes

Sessions

Notes

Overview

Sessions are a way to preserve certain data across subsequent accesses.

In A Nutshell

  • Unique identifier (session id);
  • Server side;
  • Easy to use;
  • Built-in.

A few use cases:

  • Keeping user authentication and roles;
  • Storing items into a cart;
  • Storing a flash message between redirections.

Notes

Code Please

// Initalize session
session_start();

// Regenerate identifier
session_regenerate_id();

// Assign "key" to `$value`
$_SESSION['key'] = $value;

// Retrieve "key"
$_SESSION['key'];

// Terminate session
session_destroy();

Session should be started before headers are sent! http://php.net/manual/en/book.session.php.

Notes

Security Concerns

  • Prediction (guessing a valid session identifier);
  • Man in the Middle (capturing a valid session identifier);
  • Session Fixation (attacker chooses the session identifier);
  • Session Hijacking (all attacks that attempt to gain access to another user's session).

Workarounds

  • Regenerate ids when authentication changes;
  • Bind sessions to IP addresses;
  • Define expiration/timeout;
  • Don't rely on the default settings;
  • Use HTTPS.

Notes

Session Configuration

An example of PHP session configuration that is more secure:

; Helps mitigate XSS by telling the browser not to expose the cookie to
; client side scripting such as JavaScript
session.cookie_httponly = 1

; Prevents session fixation by making sure that PHP only uses cookies for
; sessions and disallow session ID passing as a GET parameter
session.use_only_cookies = 1

; Better entropy source
; Evades insufficient entropy vulnerabilities
session.entropy_file = "/dev/urandom"
; `session.entropy_length` might help too!

; Smaller exploitation window for XSS/CSRF/Clickjacking...
session.cookie_lifetime = 0

; Ensures session cookies are only sent over secure connections (it requires
; a valid SSL certificate)
; Related to OWASP 2013-A6-Sensitive Data Exposure
session.cookie_secure = 1

Notes

Authentication

Notes

What You Have Right Now

No Authentication/Security Layer, anyone can access everything.



Notes

The Big Picture


Notes

The Interceptor Pattern

The Security Layer, as seen before, has to intercept the process of converting a request into a response in order to perform some checks.

You need a way to hook into this process before invoking the controller: Interceptor Pattern to the rescue!

The Interceptor Pattern allows you to execute some code during the default application's lifecyle.

A way to implement this pattern is to use events. It is more or less like the Observer/Observable pattern.

Event Dispatcher

The application notifies a set of listeners to an event. The listeners can register themselves to a particular event. An Event Dispatcher manages both the listeners, and the events.

Notes

Introducing the Event Dispatcher

Simple event dispatcher using a trait:

trait EventDispatcherTrait
{
    private $events = [];

    public function addListener($name, $callable)
    {
        $this->events[$name][] = $callable;
    }

    public function dispatch($name, array $arguments = [])
    {
        foreach ($this->events[$name] as $callable) {
            call_user_func_array($callable, $arguments);
        }
    }
}

For a more complete event dispatcher implementation, see symfony/event-dispatcher component

Notes

Using the EventDispatcherTrait

In order to intercept the process described before, you have to notify some listeners once you enter in the process() method by dispatching the event:

class App
{
    use EventDispatcherTrait;

    ...

    private function process(Request $request, Route $route)
    {
        $this->dispatch('process.before', [ $request ]);

        ...
    }
}

The listeners have to listen to this event:

$app->addListener('process.before', function (Request $request) {
    // code to execute
});

Notes

A simple Firewall

Now that you can hook into the application's lifecycle, you can write a basic but powerful Firewall.

$app->addListener('process.before', function (Request $request) use ($app) {
    if ($request->guessBestFormat() !== 'application/json') {
        return;
    }

    if (empty($_SERVER['PHP_AUTH_USER'])) {
        header('WWW-Authenticate: Basic realm="Super secure app"');
        throw new \Exception\HttpException(401);
    }

    if ($_SERVER['PHP_AUTH_USER'] !== 'admin'
     || $_SERVER['PHP_AUTH_PW'] !== 'not-so-secure') {
        throw new \Exception\HttpException(401);
    }
});

If authentication fails, the server should return a 401 status code.

This Firewall implements a stateless authentication mecanism: Basic HTTP authentication.

Notes

Stateless Authentication

Useful for API authentication.

OpenID (in stateless mode)

http://openid.net/

Basic and Digest Access Authentication

http://pretty-rfc.herokuapp.com/RFC2617

WSSE Username Token

http://www.xml.com/pub/a/2003/12/17/dive.html

Notes

Statefull Firewall (1/2)

This firewall needs a whitelist of unsecured routes (i.e. routes that don't require the user to be authenticated) associated with their allowed HTTP methods:

$allowed = [
    '/login'     => [ Request::GET, Request::POST ],
    '/locations' => [ Request::GET ],
];

Never Blacklist; Only Whitelist

Notes

Statefull Firewall (2/2)

The Firewall leverages the session to determine whether the user is authenticated or not:

session_start();

if (isset($_SESSION['is_authenticated'])
    && true === $_SESSION['is_authenticated']) {
    return;
}

Notes

Implementing The Firewall

$app->addListener('process.before', function(Request $req) use ($app) {
    if ($req->guessBestFormat() === 'application/json') {
        return;
    }

    session_start();

    $allowed = [
        '/login' => [ Request::GET, Request::POST ],
    ];

    if (isset($_SESSION['is_authenticated'])
        && $_SESSION['is_authenticated'] === true) {
        return;
    }

    foreach ($allowed as $pattern => $methods) {
        if (preg_match(sprintf('#^%s$#', $pattern), $req->getUri())
            && in_array($req->getMethod(), $methods)) {
            return;
        }
    }

    return $app->redirect('/login');
});

Notes

Authentication Mechanism


Notes

Adding New Routes

$app->get('/login', function () use ($app) {
    return $app->render('login.php');
});

$app->post('/login', function (Request $request) use ($app) {
    $user = $request->getParameter('user');
    $pass = $request->getParameter('password');

    if ($user === 'root' && $pass === 'quarante-deux') {
        $_SESSION['is_authenticated'] = true;

        return $app->redirect('/');
    }

    return $app->render('login.php', [ 'user' => $user ]);
});

$app->get('/logout', function (Request $request) use ($app) {
    session_destroy();

    return $app->redirect('/');
});

Notes

Basic Security Thinking

  1. Trust nobody and nothing;
  2. Assume a worse-case scenario;
  3. Apply Defense-In-Depth;
  4. Keep It Simple Stupid (KISS);
  5. Principle of Least Privilege;
  6. Attackers can smell obscurity;
  7. RTFM but never trust it;
  8. If it is not tested, it does not work;
  9. It is always your fault!

Survive The Deep End: PHP Security

Notes

Writing Better Code

Notes

Agenda

  • Coding Standards
  • Programming To The Interface
  • Dependency Inversion Principle (DIP)
  • Dependency Injection (DI)
  • Inversion of Control (IoC)
  • Dependency Injection Container (DIC)
  • Component Driven Development
  • From STUPID to SOLID code!
  • Object Calisthenics

Notes

Coding Standards

<?php

namespace Vendor\Model;

class Foo
{
    const VERSION = '1.0';

    public $bar = null;

    protected $opts;

    private $var3 = 123;

    public function __construct(BarInterface $bar, array $opts = [])
    {
        $this->bar  = $bar;
        $this->opts = $opts;
    }
}

Read more about coding standards with PSR-1 and PSR-2.

Notes

PHP Coding Standards Fixer

The PHP Coding Standards Fixer tool fixes most issues in your code when you want to follow the PHP coding standards as defined in the PSR-1 and PSR-2 documents.

Installation

$ wget http://cs.sensiolabs.org/get/php-cs-fixer.phar

Usage

$ php php-cs-fixer.phar fix /path/to/dir/or/file

More information at: http://cs.sensiolabs.org/.

Notes

Programming To The Interface

Reduces dependency on implementation specifics and makes code more reusable.

The BananaController can use either Twig or the raw PHP implementation as template engine thanks to the TemplateEngine interface:

interface TemplateEngine
{
    /**
     * @param string $template
     * @param array  $parameters
     *
     * @return string
     */
    public function render($template, array $parameters = []);
}

You should think about interfaces, not about internal implementation details.

Notes

Dependency Inversion Principle (DIP) (1/3)

The Dependency Inversion Principle has two parts:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions;
  • Abstractions should not depend upon details. Details should depend upon abstractions.

DIP is about the level of the abstraction in the messages sent from your code to the thing it is calling.

Notes

Dependency Inversion Principle (2/3)

In conventional architectures, higher level components rely on lower level ones.



Notes

Dependency Inversion Principle (3/3)

In a direct application of dependency inversion, the interfaces are owned by the upper/policy layers.

Notes

Dependency Injection (DI)

Dependency Injection is about how one object acquires a dependency.

class Foo
{
    private $bar;

    // **NOT** DI
    public function __construct()
    {
        $this->bar = new Bar();
    }
}

When a dependency is provided externally, then the system is using DI:

// DI!
public function __construct(Bar $bar)
{
    $this->bar = $bar;
}

Notes

Dependency Injection (Anti) Pattern

ServiceLocator

The basic idea behind a service locator is to have an object that knows how to get hold of all of the services that an application might need.

class ServiceLocator
{
    public static function getBar()
    {
        return new Bar();
    }
}

class Foo
{
    private $bar;

    public function __construct()
    {
        $this->bar = ServiceLocator::getBar();
    }
}

Notes

Dependency Injection Patterns

Constructor Injection

All dependencies are injected using a constructor:

class Foo
{
    private $bar;

    public function __construct(BarInterface $bar)
    {
        $this->bar = $bar;
    }
}

Notes

Dependency Injection Patterns

Setter Injection

Dependencies are injected through setters:

class Foo
{
    private $bar;

    public function setBar(BarInterface $bar)
    {
        $this->bar = $bar;
    }
}

Notes

Dependency Injection Patterns

Interface Injection

An interface describes the injection:

interface BarAware
{
    public function setBar(BarInterface $bar);
}

It needs to be implemented by the class that wants to use a BarInterface:

class Foo implements BarAware
{
    private $bar;

    public function setBar(BarInterface $bar)
    {
        $this->bar = $bar;
    }
}

Notes

Inversion of Control (IoC)

Inversion of Control is about who initiates the call. If your code initiates a call, it is not IoC, if the container/system/library calls back into code that you provided it, it is IoC.

Hollywood Principle: Don't call us, we'll call you.

Notes

Dependency Injection Container (DIC)

A framework or library for building graphs of objects by passing in (injecting) each object's dependencies. Object lifetimes are handled by the container instead of by the consuming object.

Most of the time, you rely on configuration files to describe your classes and their dependencies. A class in this context is also known as a service.

You ask this container to retrieve a service, and it is lazy loaded and dynamically built:

// It's an instance of `TemplateEngine`, but you don't know
// anything about its internal implementation.
// Is it the raw PHP implementation or Twig?
$engine = $container->get('template_engine');

Do you need a dependency injection container?

Notes

PHP Implementations

Twittee

Twittee is the smallest Dependency Injection Container written in PHP. It fits in a tweet (less than 140 characters):

class Container {
    protected $s = [];
    function __set($k, $c) { $this->s[$k] = $c; }
    function __get($k) { return $this->s[$k]($this); }
}

$c = new Container();
$c->mailer_config = ['smtp' => '…', 'port' => 25];
$c->mailer = function ($c) { return new Mailer($c->mailer_config); };

Notes

PHP Implementations

Pimple

Pimple is a small Dependency Injection Container for PHP 5.3 that consists of just one file and one class.

The Symfony2 DependencyInjection Component

The DependencyInjection component allows you to standardize and centralize the way objects are constructed in your application.

Read more:

Notes

Component Driven Development

It’s all about Separation of Concerns (SoC).

You design components with their own logic, each component does one thing well, and only one thing.

How to manage these components in your application?

Read more: Component Driven Development: it's like Lego!

Notes

From STUPID to SOLID code! (1/2)

STUPID

  • Singleton
  • Tight Coupling
  • Untestability
  • Premature Optimization
  • Indescriptive Naming
  • Duplication

Read more: http://williamdurand.fr/2013/07/30/from-stupid-to-solid-code/#stupid-code-seriously.

Notes

From STUPID to SOLID code! (2/2)

  • Single Responsibility Principle
  • Open/Closed Principle
    • software entities should be open for extension, but closed for modification
  • Liskov Substitution Principle
    • if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the application
  • Interface Segregation Principle
  • Dependency Inversion Principle

Read more: http://williamdurand.fr/2013/07/30/from-stupid-to-solid-code/#solid-to-the-rescue.

Notes

Object Calisthenics

9 rules invented by Jeff Bay in his book The ThoughWorks Anthology:

  1. Only One Level Of Indentation Per Method
  2. Don't Use The ELSE Keyword
  3. Wrap All Primitives And Strings
  4. First Class Collections
  5. One Dot Per Line (also known as the Law of Demeter)
  6. Don't Abbreviate
  7. Keep All Entities Small
  8. No Classes With More Than Two Instance Variables
  9. No Getters/Setters/Properties

Read more: http://williamdurand.fr/2013/06/03/object-calisthenics/.

Notes



Notes

Testing

Notes

Notes

Agenda

  • Unit Testing
  • Functional Testing
  • Behavior Driven Development
  • Testing Tweet Frameworks

Notes

Unit Testing

Notes

Unit Testing

Unit testing is a Method by which individual units of source code are tested to determine if they are fit for use.

PHPUnit is the de-facto standard for unit testing in PHP projects.

Install it using Composer:

{
    "require-dev": {
        "phpunit/phpunit": "~5.7"
    }
}

Or as a PHAR:

$ wget http://pear.phpunit.de/get/phpunit.phar
$ chmod +x phpunit.phar

Notes

PHPUnit — The Rules

The tests for a class Class go into a class ClassTest.

ClassTest should inherit from PHPUnit_Framework_TestCase, but a common practice is to create a TestCase class for a project, and to inherit from it:

class TestCase extends PHPUnit_Framework_TestCase {}

The tests are public methods that are named test*, but you can also use the @test annotation:

class ClassTest extends TestCase
{
    public function testFoo()
    {
        $object = new MyClass();
        $this->assertEquals('foo', $object->foo(), 'optional comment');
    }
}

Notes

PHPUnit — Assertions

  • assertEquals()
  • assertTrue()
  • assertFalse()
  • etc.

See all assertion methods: http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html.

Notes

Running PHPUnit

Running the test suite:

$ phpunit
PHPUnit 3.7.0 by Sebastian Bergmann.

.

Time: 1 seconds, Memory: 5.25Mb

OK (1 test, 1 assertion)

Getting the code coverage:

$ phpunit --coverage-text

See also:

$ phpunit --log-junit junit_output.xml

$ phpunit --testdox

Notes

Test Double

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists;
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example);
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent' → State verification;
  • Mocks are objects pre-programmed with expectations which form a specification of the calls they are expected to receive → Behavior verification.

Notes

Functional Testing

Notes

Functional Testing

Also known as acceptance testing.

Use tools to create automated tests that actually use your application rather than only verifying that individual units of code behave correctly.

Notes

Behavior Driven Development

Notes

Behavior Driven Development

SpecBDD

  • You write specifications that describe how your actual code should behave;
  • Focused on technical behavior;
  • PHPSpec.

StoryBDD

  • You write human-readable stories that describe the behavior of your application;
  • Business oriented;
  • Behat.

Notes

Behat

Install Behat via Composer:

{
    "require-dev": {
        "behat/behat": "2.4.*@stable"
    },
    "config": {
        "bin-dir": "bin/"
    }
}

The bin/behat command is now installed!

Or as a PHAR:

$ wget https://github.com/downloads/Behat/Behat/behat.phar
$ chmod +x behat.phar

Notes

Using Behat (1/2)

Initialize your project:

$ bin/behat --init

Define a Feature:

# features/your_first_feature.feature
Feature: Your first feature
  In order to start using Behat
  As a manager or developer
  I need to try

Define a Scenario:

Scenario: Successfully describing scenario
  Given there is something
  When I do something
  Then I should see something

Notes

Using Behat (2/2)

Executing Behat:

$ behat

Writing your Step definitions:

/**
 * @Given /^there is something$/
 */
 public function thereIsSomething()
 {
    throw new PendingException();
 }

Must Read: https://speakerdeck.com/everzet/behat-by-example.

Read more about Behat: http://docs.behat.org/.

Notes

Awesome Projects

Notes

Assert

Assertions and guard methods for input validation (not filtering!) in business-model, libraries and application low-level code.

use Assert\Assertion;
use Assert\AssertionFailedException;

try {
    \Assert\that($identifier)->notEmpty()->integer();
    Assertion::notEmpty($message, 'Message is not specified');
} catch(AssertionFailedException $e) {
    $e->getValue();         // the value that caused the failure
    $e->getConstraints();   // the additional constraints of the assertion
}

beberlei/assert

Notes

Carbon

A simple API extension for DateTime.

$tomorrow            = Carbon::now()->addDay();
$lastWeek            = (new Carbon())->subWeek();
$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London');

if (Carbon::now()->isWeekend()) {
    echo 'Party!';
}

Carbon::now()->subDays(5)->diffForHumans();    // 5 days ago

Freezing time:

Carbon::setTestNow(Carbon::create(2001, 5, 21, 12));

echo Carbon::now();   // 2001-05-21 12:00:00
                      // test, test, test!

Carbon::setTestNow(); // clear the mock
echo Carbon::now();   // 2014-02-02 21:00:00

briannesbitt/Carbon

Notes

Faker

Fake data generator.

// use the factory to create a `Faker\Generator` instance
$faker = Faker\Factory::create();

// generate data by accessing properties
echo $faker->name;
// 'Lucy Cechtelar';
echo $faker->address;
// "426 Jordy Lodge
// Cartwrightshire, SC 88120-6700"
echo $faker->text;
// Sint velit eveniet. Rerum atque repellat voluptatem quia rerum. Numquam
// beatae sint laudantium consequatur. Magni occaecati itaque sint et sit
// tempore. Nesciunt amet quidem. Iusto deleniti cum autem ad quia aperiam.
echo $faker->email;
// 'tkshlerin@collins.com'
echo $faker->ipv4;
// '109.133.32.252'
echo $faker->creditCardNumber;
// '4485480221084675'

fzaninotto/Faker

Notes

Flysystem

Filesystem abstraction layer.

use League\Flysystem as F

// Adapters: Local, Amazon Web Services - S3, Rackspace Cloud Files,
//           Dropbox, Ftp, Sftp, Zip, WebDAV
$filesystem = new F\Filesystem(new F\Adapter\Local(__DIR__.'/path/to/dir'));

// Create a file
$filesystem->write('path/to/file.txt', 'contents');

// Update a file
$filesystem->update('file/to/update.ext', 'new contents');

// Write or update a file
$filesystem->put('filename.txt', 'contents');

// Delete a file
$filesyste->delete('delete/this/file.md');

// Check if a file exists
$exists = $filesystem->has('filename.txt');

thephpleague/flysystem

Notes

Mockery

// The PHPUnit Way
$mock = $this->getMock('SomeClass');
$mock->expects($this->once())
    ->method('getName')
    ->will($this->returnValue('John Doe'));

$mock2 = $this->getMock('AnotherClass');
$mock2->expects($this->any())
    ->method('getNumber')
    ->with(2)
    ->will($this->returnValue(2));
$mock2->expects($this->any())
    ->method('getNumber')
    ->with(3)
    ->will($this->returnValue(3));

// The Mockery Way
$mock = \Mockery::mock('SomeClass');
$mock->shouldReceive('getName')->once()->andReturn('John Doe');

$mock2 = \Mockery::mock('AnotherClass');
$mock2->shouldReceive('getNumber')->with(2)->andReturn(2);
$mock2->shouldReceive('getNumber')->with(3)->andReturn(3);

padraic/mockery

Notes

Option Type for PHP

use PhpOption\Option;

class MyRepository
{
    public function findSomeEntity($criteria)
    {
        return Option::fromValue($this->em->find(...));
    }
}

You always Require an Entity in Calling Code

// returns entity, or throws exception
$entity = $repository->findSomeEntity(...)->get();

Fallback to Default Value If Not Available

$entity = $repository->findSomeEntity(...)->getOrElse(new Entity());

schmittjoh/phpoption

Notes

PHP-Parser

A PHP parser written in PHP, producing Abstract Syntax Trees (AST).

<?php

echo 'Hi', 'World';

array(
    0: Stmt_Echo(
        exprs: array(
            0: Scalar_String(
                value: Hi
            )
            1:
            Scalar_String(
                value:
                World
            )
        )
    )
)

nikic/PHP-Parser

Notes


React

Event-driven, non-blocking I/O with PHP: http://reactphp.org/.

Notes

Swiftmailer

Comprehensive mailing tools for PHP.

// Create the message
$message = Swift_Message::newInstance()
    // Give the message a subject
    ->setSubject('Your subject')
    // Set the From address with an associative array
    ->setFrom([ 'john@doe.com' => 'John Doe' ])
    // Set the To addresses with an associative array
    ->setTo([ 'receiver@domain.org', 'other@domain.org' => 'A name' ])
    // Give it a body
    ->setBody('Here is the message itself')
    ;

// Create the Transport
$transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25);

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

// Send the message
$result = $mailer->send($message);

swiftmailer/swiftmailer

Notes

Whoops

PHP errors for cool kids.

filp/whoops

Notes

Embracing Open Source

Notes

Notes

Notes

Golden Rules

  • Read other people's code is the fastest way to learn;
  • Think about what you have to do;
  • Read the code, not the doc;
  • Always ask "why?";
  • Never trust the user;
  • Think again;
  • Simple is always better than complicated;
  • Keep your code readable;
  • Test your code, it eases refactoring;
  • Keep HTTP protocol in mind.

Notes

The End.

Notes

Well... Maybe Not.
PHP Extended

Notes

Fork me on GitHub