2015-10-22-ZF3



2015-10-22-ZF3

1 2


2015-10-22-ZF3


On Github weierophinney / 2015-10-22-ZF3

10 Years of Zend Framework

We started work on ZF 10 years ago, and announced it at the very first ZendCon. At the time, we were working with a group of selected industry partners and community members. We did an initial release the following March, at which time we opened up development to the world via a public subversion repository.

Original Architecture

  • Components
  • Bound together by an MVC

Evolution of v1

  • Forms
  • Layouts
  • Zend_Application
  • Dojo, jQuery integration

By 1.10, we had a stable stack that was solving a majority of problems as encountered by our users, and had tools to allow them to rapidly build applications.

Revolution: ZF2

  • Event-driven architecture
  • Dependency injection
  • First class modules
  • Events gave users a way to hook into every part of the ZF workflow, or to build their own workflows.
  • Dependency injection gave users a way to substitute different classes or implementations where desired
  • Modules were something we dreamed of in ZF1, but never accomplished. In ZF2, we made them first-class citizens. Modules enabled code re-use and granularity; coupled with the events system and dependency injection, they also enabled a ton of customizability.

… at a cost

  • Complete break in compatibility.
  • Targeting expert developers == less inviting
  • Components depend on integration provided by MVC layer

Ecosystem changes

Composer

Composer launched as we were finalizing the stable ZF2 release, so ZF2 has always lived in that ecosystem. However, we didn't design for it, which meant that we were manually splitting the components, and often those releases were delayed from an official ZF2 release.

FIG

Framework Interop Group

New standards such as logging and PSR-7 pointed a new direction for FIG: shared interfaces, allowing developers to decouple their code from specific implementations.

PSR-7

Every framework abstracts HTTP differently.

Let's standardize that.

Frameworks must change

The job of a framework is to provide plumbing, and get out of your way.

ZF3 Goals

  • Componentization and re-use
  • Performance
  • Usability
  • Focus on PSR-7 and middleware

These goals are intertwined. PSR-7 and middleware are more performant, provide simpler usage paradigms, and encourage mixing and matching individual components.

What we've accomplished

Components

All components are now versioned separately.

(Full story at bit.ly/zf2-split)

This is huge. Many components have continued to get version bumps despite having zero changes between versions. Other components have been held back, because they have to wait for the rest of the framework to be ready.

Today, we can version these as needed. Already, we have advanced versions of zend-log and zend-servicemanager that incorporate new features; some, such as servicemanager and eventmanager, are about ready to be tagged at 3.0 NOW; they don't have to wait for the full framework.

Standardized Package Structure

  • PSR-4 structure for source and tests.
  • Documentation per package(in progress)
  • Standard QA toolchain; per-package continuous integration.

ZF2 Package

The zendframework package now simply depends on components.

{
  "require": {
    "zendframework/zend-authentication": "^2.5",
    "zendframework/zend-cache": "^2.5",
    "zendframework/zend-captcha": "^2.5",
    "etc": "*"
  }
}

That's it; no code! This means that as we update individual components, when you run a composer update, you get those new features. Without requiring a new version of the framework itself.

Back to Basics

ZF3 will reduce dependenciesto only what's needed for the MVC.

Use Composer to add what you need.

Change is inevitable

Service Manager

  • container-interop
  • Consistent interfaces.
  • Re-use factories for multiple named services.
  • Immutable.
  • Performant (4X faster)!
  • Mostly backwards compatible!
  • Thank Michäel Gallego!
  • We're using an emerging standard, container-interop, as the base interface of the service manager component. This makes it a drop-in replacement for other containers, but also means you can swap out service manager for something else. Use your code, the way you want.
  • All interfaces follow the same pattern of accepting the container first, and additional arguments after; additionally, the majority are invokables, meaning you can write them as any PHP callable. This also means that the abstract factory interface is now drop-in compatible with the factory interface, as it extends it.
  • Factories now receive the requested service name as an argument, allowing them to be re-used for multiple services, and return different instances based on the service requested. This largely eliminates the need for abstract factories!
  • Immutability allows us to do aggressive caching, reduced the code base by 50%, and sped it up by 4X!
  • Depending on how you write your factories, you may not need to do anything when upgrading!

New method: build

public function build($name, array $options = null)

For when you need a factory; think plugins

FactoryInterface

public function __invoke(
    ContainerInterface $container,
    $requestedName,
    array $options = null
)
  • BC break is in the new required argument, $requestedName
  • However, you can also write factories as any PHP callable, and ignore any extra arguments passed!

Forward Compatibility

Write your current factories as invokables.

public function __invoke(
    ContainerInterface $container
)
  • Not using the factory interface means that with the new required arguments, you no longer have to make changes.
  • Typehinting on ContainerInterface works NOW (as of 2.6), making it forward compatible.
  • Apigility is already doing this!

EventManager

  • 4X–15x performance based on use case!
  • BC breaks:
    • Removed argument overloading for trigger().
    • Aggregate attachment is moved to aggregate implementations.
  • Overloading added complexity to:
    • using the method
    • internally in parsing the arguments

Triggers

trigger($eventName, $target = null, $argv = []);
triggerUntil(callable $callback, $eventName, $target = null, $argv = []);
triggerEvent(EventInterface $event);
triggerEventUntil(callable $callback, EventInterface $event);
  • Added the three additional methods to 2.7; you can make code forward-compatible now.
  • No more need to remember argument order, or what an argument means based on position or type or other arguments provided.
  • Clear indication when triggering that you will be testing return values.
  • Consistency in methods.

Aggregates

Before:

$events->attach($aggregate);
$events->attachAggregate($aggregate);
$aggregate->attach($events);

After:

$aggregate->attach($events);
  • Takeaway: you can make your code forward-compatible by only using the last signature!
  • TMTOWTDI often bites you in the ass. Having exactly one way to do it, poka-yoke takes guess work away.

MVC

  • Updated to changes in zend-servicemanager.
  • Updated to changes in zend-eventmanager.
  • Essentially stays the same.
  • But adds a MiddlewareListener.

Routing to Middleware

'oauth' => [
    'type' => 'Literal',
    'options' => [
        'route' => '/oauth',
        'defaults' => [
            'middleware' => OAuthMiddleware::class,
        ],
    ],
],

MVC Availability

4–6 weeks!

  • We need to get components updated to the EM and SM changes before we release to ensure everything works well together.
  • We need to finalize the list of components we include by default.
  • We need to document how to add components, and wire them into the framework.

Enough with the boring stuff

That's still the MVC as we know it, just faster. This is what many are thinking of as the ZF3 release. It's not.

Time to Reflect

Where is the PHP community headed?

We already did this reflection, but let'd re-focus.

Composer

One simply does not release a package without making it availble via Composer. Even most SDKs are available via Composer and packagist anymore.

FIG

Creating shared interfaces.

Users can target abstractions, not implementations.

PSR-7

HTTP Message Interfaces

Providing a common HTTP abstraction to program against.

Middleware

Think of this in terms of the unix philosophy: do one thing well, and communicate via streams.

Middleware Signatures

function (ServerRequestInterface $request) : ResponseInterface

function (
    ServerRequestInterface $request,
    ResponseInterface $response
) : ResponseInterface

function (
    ServerRequestInterface $request,
    ResponseInterface $response,
    callable $next
) : ResponseInterface
Primary goal is to transform a request into a response; the main differences between the signatures is that each successive one reduces depending on concrete implementations and/or using convention-based approaches.

container-interop

interface ContainerInterface
{
    public function has($serviceName);
    public function get($serviceName);
}
There will be more infrastructure interfaces like this in the future, too.

Takeaways

When we consider all these points — composer, fig, PSR-7, middleware, container-interop — what do we learn?

Frameworks should be an implementation detail

Frameworks should be a commodity

Frameworks should get out of the way of your code

So, if the job is to get out of your way, what will an application stack look like in the future? How will developers program applications in PHP? Our answer…

Expressive

PSR-7 middleware microframework

  • Provides and consumes a routing interface.
  • Pulls matched middleware from a ContainerInterface.
  • Provides a templating interface, if you need it.
  • Provides error handling, and a way to hook into it.
  • Providing and coding to interfaces makes everything swappable.

In action:

Expressive

Wiring together commodity components.

Own your codebase

ZF3 is a Movement

An end to framework silos.

Thank you!

Matthew Weier O'Phinney

http//framework.zend.comhttps://apigility.orghttps://mwop.net @mwop

Rate this talk: https://joind.in/15622