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
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.
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.
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.
Expressive
Wiring together commodity components.
ZF3 is a Movement
An end to framework silos.