PHP-FIG

Part of a balanced PHP diet

Presented by Larry Garfield (@Crell)

@Crell

Larry implements Huggable
  • D.R.I.E.S., Platform.sh
  • Drupal 8 Web Services Lead
  • Drupal Representative, PHP-FIG
  • implements Huggable

What is PHP-FIG?

The Framework Interoperability Group
(Credit: @ninjagrlstuff)

What is PHP-FIG?

An open question...

FIG 1.0

In the beginning,
PHP was without form and void...

The PHP Standards Group

php|tek, May 2009

We're going to have these namespace things,
let's use them the same way for a change!
The smoke filled room where FIG was founded.

(Smoke not guaranteed)

PSR-0

  • One autoloading standard/convention
  • Java-inspired
  • PEAR-inspired
  • Rudimentary, but enough

Worst

Launch

Ever

Thrown off lists.php.net, moved to Google Groups

Admitted a few new projects

Slept through 2010

Debated a lot in 2011

2012

Renamed to Framework Interoperability Group

FIG

Actually became useful!

We're a group of established PHP projects whose goal is to talk about commonalities between our projects and find ways we can work better together.

—PHP-FIG website

PSR

PHP Standard Recommendation

Scope: Any definable standard or convention that improves collaboration and interoperability.

FIG 2.0

PHP

2013

  • Projects, not people
  • Quasi-formal working groups (ish)

Projects, not people

Voted in by current voting reps

38 projects

FIG 2.0

FIG 2.0

Voting representatives vote

Everything else is open to all

The Process™

Pre-Draft

Random idea

Draft

PSR number assigned

Iterate and collaborate

Review

Release Candidate

Approved!

Profit!!1!

Staff

Editor
Owns the work, gatekeeper
Sponsors (x2)
Supporting Voting Reps
Coordinator
Secretary
Contributors
Anyone else

And 3 elected FIG Secretaries to do the paperwork

Problems

  • Projects, but still people
  • Most voting reps absentee
  • Few "domain experts"
  • For whom?

FIG 3.0

In voting now

(Disclaimer: Author speaking!)

FIG 3 reorganizes roles a bit

Working Group

Editor
Gatekeeper
Sponsor
Guidance/Core bridge
Other contributors (x3)
Develops spec

Core Committee (x12)

  • Elected
  • Not project-based
  • Focuses on big picture

FIG 3.0 Workflow

The FIG 3 workflow has more steps

Will it pass...?

Vote currently at 12:5:2

Current PSRs

"Hard" specs

  • PSR-0 (Autoloading)
  • PSR-3 (Logging)
  • PSR-4 (Autoloading)
  • PSR-6 (Caching)
  • PSR-7 (HTTP Messages)

"Soft" specs

  • PSR-1 (Basic coding standards)
  • PSR-2 (Full coding standards)

Autoloading

PSR-0 PSR-4

PSR-0


new \Crell\HtmlModel\Head\Link_Element();
// Results in:
include_once $some_root . '/Crell/HtmlModel/Head/Link/Element.php';
          

PSR-4


new \Crell\HtmlModel\Head\Link_Element();
// Results in:
include_once $some_root . '/Head/Link_Element.php';
          

The prefix is externally configured

Composer


{
  "name": "crell/htmlmodel",
  "autoload": {
    "psr-4": {
      "Crell\\HtmlModel\\": "src"
    }
  }
}
        

Always use PSR-4, not PSR-0

(Shameless Plug for HtmlModel)

Logging

PSR-3

Logger


namespace Psr\Log;

interface LoggerInterface {
  public function log($level, $message, array $context = array());

  public function emergency($message, array $context = array());
  public function alert($message, array $context = array());
  public function critical($message, array $context = array());
  public function error($message, array $context = array());
  public function warning($message, array $context = array());
  public function notice($message, array $context = array());
  public function info($message, array $context = array());
  public function debug($message, array $context = array());
}
        

Log levels


namespace Psr\Log;

class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
        

Based on RFC 5424

Context?

Extra metadata, including placeholders


$logger->notice("User {$username} failed to login. Hacker, maybe?");
        

$username = "'; DROP TABLE students; --";
        

$username = "<script>alert('Bobby Tables is so cute.');</script>";
        

Can't know output target in advance

Secure logging


$logger->notice("User {username} failed to login. Hacker, maybe?", [
  'username' => $username,
]);
        

Inspired by Drupal watchdog()/t()

Logging exceptions


try {
  do_something_dangerous();
}
catch (\Exception $e) {
  $logger->error("Well that escalated quickly.", ['exception' => $e]);
}
        

Caching

PSR-6

PSR-6

Similar goal as Logging

\Psr\Cache

  • CacheItemInterface
  • CacheItemPoolInterface

Repository model

Simple caching


class MyService {
  protected $pool;

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

  public function getWidgetList() {
    $item = $this->pool->getItem('widget_list');

    if (!$item->isHit()) {
      $value = $this->computeExpensiveWidgetList();

      $item
        ->set($value)
        ->expiresAt(new DateTime('now +1 hour'));

      $this->pool->save($item);
    }
    return $item->get();
  }
}
        

Bulk operations


class MyService {
  protected $pool;

  // ...

  public function warmCache() {
    foreach ($this->widgets as $widget) {

      $item = $this->pool->getItem('widget-' . $widget->id());

      $item->set($this->computeExpensiveValue($widget));

      $this->pool->saveDeferred($item);
    }
    $this->pool->commit();
  }

  public function getWidgetInfo(array $ids) {
    $list = array_map(function($id) {return 'widget-' . $id; }, $ids);

    return $this->pool->getMultiple($list);
  }
}
        

Read-through caching


interface ImportantInterface {
  public function doExpensiveStuff($param);
}

class Important implements ImportantInterface {
  public function doExpensiveStuff($param) {
    // ...
  }
}

class CacheWrapper implements ImportantInterface {
  public function __construct(CacheItemPoolInterface $pool, ImportantInterface $wrapped) {
    $this->pool = $pool;
    $this->wrapped = $wrapped;
  }

  public function doExpensiveStuff($param) {
    $item = $this->pool->getItem(__CLASS__ . __METHOD__);

    if (!$item->isHit()) {
      $item->set($this->wrapped->doExpensiveStuff($param));
      $this->pool->save($item);
    }

    return $item->get();
  }
}
        

Extensible

  • Add interface to both pool and item
  • Tagging, namespaces, etc.

HTTP Messages

PSR-7

CGI sucks

Common Gateway Interface

  • NCSA, 1993
  • "specification for calling command line executables"
  • Pass environment variables to script
  • $_SERVER

Super globals!

Picard-Riker Face Palm

HTTP Messages should be objects!

(Message passing, yo.)

\Psr\Http\Message

  • MessageInterface
    • RequestInterface
      • ServerRequestInterface
    • ResponseInterface
  • StreamInterface
  • UriInterface
  • UploadedFileInterface

Immutable objects


$response = new Response();

$response = $response->withStatusCode(200);
$response = $response->withHeader('Content-Type', 'application/json');

$response = $response
  ->withHeader('Cache-Control', 'public')
  ->withAddedHeader('Cache-Control', 'max-age=300');
        

Streams


$body = new PsrFileStream('secret_plans.odt');
$response = $response->withBody($body);
        

$body = new StringStream($htmlData);
$response = $response->withBody($body);
        

$callback = function() use ($data) {
  foreach ($data as $record) {
    return implode(', ', $record);
  }
};

$body = new CallbackStream($callback);
$response = $response->withBody($body);
        

UriInterface

URIs are way more complicated than you think

More portable and predictable than parse_url()

Coding standards

PSR-1 PSR-2

The most needlessly controversial specs ever

PSR-1

Runtime-important standards

  • No short-tags
  • UTF-8 files
  • ClassName::methodName and ClassName::CONSTANT
  • Use an autoloader
  • Declare symbols or execute code, not both

You're probably doing this already.

PSR-2

Stylistic standards

  • 4-space indentation
  • One-true-brace alignment
  • ...

That there is a standard matters more than what it is

PHP 7 updates coming in PSR-12...

In progress

  • PSR-9 (Security Disclosure)
  • PSR-10 (Security Reporting)
  • PSR-11 (Container)
  • PSR-12 (Coding Styles, again)
  • PSR-13 (Hypermedia Links)
  • PSR-14 (Event Dispatcher)
  • PSR-15 (Middlewares)
  • PSR-16 (Simplified Caching)
  • PSR-17 (HTTP Factories)

Security

PSR-9 PSR-10

Security

PSR-9
Process
Reporting mechanisms
PSR-10
Security Advisory format

Open alternative to Sensio Labs Security Advisory Checker

Am I vulnerable?

Yep  Nope

Containers

PSR-11

ContainerInterface


namespace Psr\Container;

interface ContainerInterface
{
    public function get(string $id);

    public function has(string $id);
}
        

For factories and other edge cases

And delegation

Hypermedia Links

PSR-13

Hypermedia Links


namespace Psr\Link;

interface LinkInterface
{
    public function getHref();
    public function isTemplated();
    public function getRels();
    public function getAttributes();
}
        

namespace Psr\Link;

interface LinkCollectionInterface
{
    public function getLinks();
    public function getLinksByRel($rel);
}
        

Plus evolvable extensions

Event Manager

PSR-14

Still early, over-complicated

HTTP Message tooling

PSR-15 PSR-17

HTTP Factories

  • RequestFactoryInterface
  • ResponseFactoryInterface
  • ServerRequestFactoryInterface
  • StreamFactoryInterface
  • UploadedFileFactoryInterface
  • UriFactoryInterface

HTTP Middleware


namespace Psr\Http\Middleware;

interface ServerMiddlewareInterface extends MiddlewareInterface
{
    public function process(ServerRequestInterface $request, DelegateInterface $frame);
}

interface DelegateInterface
{
    public function next(RequestInterface $request);
}
        

class Foo implements ServerMiddlewareInterface
{
    // ... Constructor here.

    public function process(ServerRequestInterface $request, DelegateInterface $delegate) {
        // Pass on the request to the next middleware.
        $response = $delegate->next($request);

        // Or create one yourself.
        return $this->responseFactory->createResponse(
            $this->streamFactory->createTempStream('Hello world'),
            200
        );
    }
}
        

Simplified caching

PSR-16


namespace Psr\SimpleCache;

interface CacheInterface
{
    public function get($key);
    public function set($key, $value, $ttl = null);
    public function delete($key);
    public function clear();
    public function getMultiple($keys);
    public function setMultiple($items, $ttl = null);
    public function deleteMultiple($keys);
    public function exists($key);
}
        

Easy "wrapper" for PSR-6

The goal of FIG is to encourage collaboration

Collaboration builds interoperability

... and community

Success

Spec Downloads Dependants
PSR-3 38 million 1943
PSR-6 829,000 165
PSR-7 11 million 711

(Source, Packagist.org, September 2016)

Our biggest success...

Huggable

PSR-8


namespace Psr\Hug;

interface Huggable
{
    public function hug(Huggable $h);
}

interface GroupHuggable extends Huggable
{
  public function groupHug($huggables);
}
        

Bulgaria PHP Draft implementation

Larry Garfield

Director of Runtimes and Integrations Platform.sh

Continuous Deployment Cloud Hosting

Stalk us at @PlatformSH