PHP-FIG

Part of a balanced PHP diet

Presented by Larry Garfield (@Crell)

@Crell

Larry implements Huggable

What is PHP-FIG?

The Framework Interoperability Group
(Credit: @ninjagrlstuff)
PHP
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

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

PSR-0

<vendor>\Package\Whatever

new \Some\Class\Name_Here();
include_once $some_root . '/Some/Class/Name/Here.php';

new Guzzle\Http\Client();
// require('/some/root/path/Guzzle/Http/Client.php');
            

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!

A real process!

Only took until 2013...

So who are you, exactly?

Projects, not people

Voted in by current voting reps

42 projects and one community busybody

Voting representatives vote

Everything else is open to all

So what does FIG, um, do?

PSR

PHP Standard Recommendation

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

How's it work?

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

Current PSRs

"Hard" specs

  • PSR-0 (Autoloading)
  • PSR-3 (Logging)
  • PSR-4 (Autoloading)
  • 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

LogerAware


namespace Psr\Log;

interface LoggerAwareInterface
{
    public function setLogger(LoggerInterface $logger);
}
            

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

HTTP Messages

PSR-7

Go to Beau's section next!

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

(Eventually)

Upcoming PSRs

"Hard" specs

  • PSR-5 (PHPDoc)
  • PSR-6 (Caching)
  • PSR-9 (Security Disclosure)
  • PSR-11 (Container)

"Soft" specs

  • PSR-10 (Security Advisories)
  • PSR-12 (Coding Styles, again)

PHP Doc

PSR-5

PSR-5

  • A formal spec for PHPDoc
  • Looooong
  • May be split up?

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.

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

(So far)

For factories and other edge cases

And delegation

The goal of FIG is to encourage collaboration

Collaboration builds interoperability

... and community

Success

PSR-3

  • 18 million installs
  • 1277 dependants

PSR-7

  • 1.4 million installs
  • 278 dependants
  • middlewares

(Source, Packagist.org, November 2015)

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

Senior Architect, Palantir.net

Let's Make Something Good Together.

Keep tabs on our work at @Palantir