SlideShare a Scribd company logo
@asgrim
Kicking off with Zend Expressive
and Doctrine ORM
James Titcumb
PHP South Africa 2018
Follow along (optional): https://github.com/asgrim/book-library
$ whoami
James Titcumb
www.jamestitcumb.com
www.roave.com
@asgrim
Follow along (optional): https://github.com/asgrim/book-library
@asgrim
What is Zend Expressive?
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
PSR-7
HTTP Message Interfaces
@asgrim
HTTP Request
POST /talk HTTP/1.1
Host: phpminds.org
foo=bar&baz=bat
@asgrim
HTTP Response
HTTP/1.1 200 OK
Content-Type: text/plain
This is the response body
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Diactoros
PSR-7 implementation
@asgrim
Node http.Server using Diactoros
$server = ZendDiactorosServer::createServer(
function ($request, $response, $done) {
return $response->getBody()->write('hello world');
},
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
$server->listen();
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Stratigility
Creating & dispatching middleware pipelines
@asgrim
So what is “middleware”?
@asgrim
Middleware in the middle
function(Request $request, RequestHandler $handler) : Response
{
// ... some code before ...
$response = $handler->handle($request);
// ... some code after ...
return $response;
}
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
psr/http-server-middleware
@asgrim
Middleware using MiddlewareInterface
<?php
declare(strict_types=1);
namespace AppMiddleware;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use PsrHttpServerMiddlewareInterface;
use PsrHttpServerRequestHandlerInterface;
final class MyMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $requestHandler
) : ResponseInterface {
return $requestHandler>handle($request);
}
}
@asgrim
Double-pass middleware
public function __invoke(
Request $request,
Response $response,
callable $next
): Response {
return $next($request, $response);
}
!!! DEPRECATED !!!
@asgrim
Modifying the response
function(Request $request, RequestHandler $handler) : Response
{
$response = $handler->handle($request);
return $response->withHeader(
'X-Clacks-Overhead',
'GNU Terry Pratchett'
);
}
@asgrim
Pipe all the things!
$pipe = new ZendStratigilityMiddlewarePipe();
$pipe->pipe($logUncaughtErrorsMiddleware);
$pipe->pipe($sessionInitialisingMiddleware);
$pipe->pipe($authenticationMiddleware);
$pipe->pipe('/', $indexHandlerMiddleware);
$pipe->pipe(new NotFoundHandler(...));
@asgrim
LogErrorsCaughtMiddleware
function(Request $request, RequestHandler $handler) : Response
{
try {
return $handler->handle($request);
} catch (Throwable $throwable) {
// Log the error, handle it with error page etc.
return new JsonResponse(
[
'message' => $throwable->getMessage(),
],
500
);
}
}
@asgrim
SessionInitialisingMiddleware
function(Request $request, RequestHandler $handler) : Response
{
session_start();
return $handler->handle($request);
}
@asgrim
function(Request $request, RequestHandler $handler) : Response
{
if (!$this->doSomeOAuthCheck($request)) {
return new JsonResponse(
[
'message' => 'Invalid OAuth token',
],
403
);
}
return $handler->handle($request);
}
AuthenticationMiddleware
@asgrim
IndexHandlerMiddleware
function(Request $request, RequestHandler $handler) : Response
{
// ... some code ...
return new JsonResponse($someData, 200);
}
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Expressive
One Ring to bring them all and in the darkness bind them.
@asgrim
Routing
@asgrim
container-interop
@asgrim
Optionally, templating
@asgrim
Piping and Routing
@asgrim
Zend Framework 2/3
What of them?
@asgrim
Middleware vs MVC
@asgrim
Using ZF components
in Expressive
@asgrim
ZF’s Module.php
class Module
{
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
}
@asgrim
ConfigProvider
@asgrim
ConfigProvider
namespace ZendForm;
class ConfigProvider
{
public function __invoke()
{
return [
'dependencies' => $this->getDependencyConfig(),
'view_helpers' => $this->getViewHelperConfig(),
];
}
}
@asgrim
ConfigProvider#getDependencyConfig()
public function getDependencyConfig()
{
return [
'abstract_factories' => [
FormAbstractServiceFactory::class,
],
'aliases' => [
'ZendFormAnnotationFormAnnotationBuilder' => 'FormAnnotationBuilder',
AnnotationAnnotationBuilder::class => 'FormAnnotationBuilder',
FormElementManager::class => 'FormElementManager',
],
'factories' => [
'FormAnnotationBuilder' => AnnotationAnnotationBuilderFactory::class,
'FormElementManager' => FormElementManagerFactory::class,
],
@asgrim
ZendForm’s Module.php (for Zend Framework)
class Module
{
public function getConfig()
{
$provider = new ConfigProvider();
return [
'service_manager' => $provider->getDependencyConfig(),
'view_helpers' => $provider->getViewHelperConfig(),
];
}
}
@asgrim
config/config.php
<?php
$aggregator = new ConfigAggregator([
ZendFormConfigProvider::class,
// .. other config ...
], $cacheConfig['config_cache_path']);
return $aggregator->getMergedConfig();
@asgrim
ConfigAggregator
@asgrim
config/config.php
$aggregator = new ConfigAggregator([
ZendExpressiveRouterFastRouteRouterConfigProvider::class,
ZendHttpHandlerRunnerConfigProvider::class,
new ArrayProvider($cacheConfig),
ZendExpressiveHelperConfigProvider::class,
ZendExpressiveConfigProvider::class,
ZendExpressiveRouterConfigProvider::class,
AppConfigProvider::class,
new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'),
new PhpFileProvider(realpath(__DIR__) . '/development.config.php'),
], $cacheConfig['config_cache_path']);
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Getting started
with Zend Expressive
@asgrim
https://github.com/asgrim/book-library
@asgrim
Expressive Skeleton
@asgrim
Expressive installer - start
composer create-project zendframework/zend-expressive-skeleton:3.0.0-rc1 book-library
Installing zendframework/zend-expressive-skeleton (3.0.0rc1)
- Installing zendframework/zend-expressive-skeleton (3.0.0rc1): Downloading (100%)
Created project in test
> ExpressiveInstallerOptionalPackages::install
Setting up optional packages
Setup data and cache dir
Removing installer development dependencie
@asgrim
Expressive installer - structure
What type of installation would you like?
[1] Minimal (no default middleware, templates, or assets; configuration only)
[2] Flat (flat source code structure; default selection)
[3] Modular (modular source code structure; recommended)
Make your selection (2): 2
- Copying src/App/ConfigProvider.php
@asgrim
Modular structure
zend-config-aggregator
@asgrim
Modular structure
zend-config-aggregator
@asgrim
Flat or Modular?
@asgrim
Default modular structure
src/
MyModule/
src/
ConfigProvider.php
Handler/
Entity/
Middleware/
templates/
test/
@asgrim
Expressive installer - container?
Which container do you want to use for dependency injection?
[1] Aura.Di
[2] Pimple
[3] zend-servicemanager
[4] Auryn
[5] Symfony DI Container
Make your selection or type a composer package name and version (zend-servicemanager):
- Adding package zendframework/zend-servicemanager (^3.3)
- Copying config/container.php
@asgrim
Expressive installer - router?
Which router do you want to use?
[1] Aura.Router
[2] FastRoute
[3] zend-router
Make your selection or type a composer package name and version (FastRoute):
- Adding package zendframework/zend-expressive-fastroute (^3.0.0alpha1)
- Whitelist package zendframework/zend-expressive-fastroute
- Copying config/routes.php
@asgrim
Expressive installer - template?
Which template engine do you want to use?
[1] Plates
[2] Twig
[3] zend-view installs zend-servicemanager
[n] None of the above
Make your selection or type a composer package name and version (n):
@asgrim
Expressive installer - whoops?
Which error handler do you want to use during development?
[1] Whoops
[n] None of the above
Make your selection or type a composer package name and version (Whoops): n
@asgrim
Expressive installer - run it!
$ composer serve
> php -S 0.0.0.0:8080 -t public/ public/index.php
[Thu Sep 1 20:29:33 2016] 127.0.0.1:48670 [200]: /favicon.ico
{
"welcome": "Congratulations! You have installed the zend-expressive skeleton application.",
"docsUrl": "https://docs.zendframework.com/zend-expressive/"
}
@asgrim
Create the endpoints
@asgrim
Book entity
class Book
{
/**
* @var string
*/
private $id;
/**
* @var bool
*/
private $inStock = true;
public function __construct()
{
$this->id = (string)Uuid::uuid4();
}
@asgrim
Book entity
class Book
{
/**
* @return void
* @throws AppEntityExceptionBookNotAvailable
*/
public function checkOut()
{
if (!$this->inStock) {
throw ExceptionBookNotAvailable::fromBook($this);
}
$this->inStock = false;
}
@asgrim
Book entity
class Book
{
/**
* @return void
* @throws AppEntityExceptionBookAlreadyStocked
*/
public function checkIn()
{
if ($this->inStock) {
throw ExceptionBookAlreadyStocked::fromBook($this);
}
$this->inStock = true;
}
@asgrim
FindBookByUuidInterface
interface FindBookByUuidInterface
{
/**
* @param UuidInterface $slug
* @return Book
* @throws ExceptionBookNotFound
*/
public function __invoke(UuidInterface $slug): Book;
}
@asgrim
CheckOutHandler
public function process(ServerRequestInterface $request, RequestHandler $handler): JsonResponse
{
try {
$book = $this->findBookByUuid->__invoke(Uuid::fromString($request->getAttribute('id')));
} catch (BookNotFound $bookNotFound) {
return new JsonResponse(['info' => $bookNotFound->getMessage()], 404);
}
try {
$book->checkOut();
} catch (BookNotAvailable $bookNotAvailable) {
return new JsonResponse(['info' => $bookNotAvailable->getMessage()], 423);
}
return new JsonResponse([
'info' => sprintf('You have checked out %s', $book->getId()),
]);
}
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Define application pipeline - pipeline.php
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->get(
'/book/{id}/check-out',
AppHandlerCheckOutHandler::class,
'check-out'
);
$app->get(
'/book/{id}/check-in',
AppHandlerCheckInHandler::class,
'check-in'
);
};
Define routes - routes.php
@asgrim
Adding some ORM
@asgrim
Your application
Doctrine quick overview
DB
DBAL
ORM
(EntityManager)
Entities
Finders,
Services,
...
@asgrim
container-interop-doctrine
@asgrim
Installation
$ composer require dasprid/container-interop-doctrine
Using version ^1.1 for dasprid/container-interop-doctrine
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 9 installs, 0 updates, 0 removals
- Installing doctrine/lexer (v1.0.1): Loading from cache
- Installing doctrine/inflector (v1.3.0): Loading from cache
- Installing doctrine/collections (v1.5.0): Loading from cache
- Installing doctrine/cache (v1.7.1): Loading from cache
- Installing doctrine/annotations (v1.6.0): Loading from cache
- Installing doctrine/common (v2.8.1): Loading from cache
- Installing doctrine/dbal (v2.6.3): Loading from cache
- Installing doctrine/orm (v2.6.1): Loading from cache
- Installing dasprid/container-interop-doctrine (1.1.0): Loading from cache
Writing lock file
Generating autoload files
@asgrim
src/App/ConfigProvider.php
use DoctrineORMEntityManagerInterface;
use ContainerInteropDoctrineEntityManagerFactory;
final class ConfigProvider
{
// ...
public function getDependencies() : array
{
return [
'factories' => [
EntityManagerInterface::class => EntityManagerFactory::class,
// ... other services
],
];
@asgrim
'doctrine' => [
'connection' => [
'orm_default' => [
'driver_class' => PDOPgSqlDriver::class,
'params' => [
'url' => 'postgres://user:pass@localhost/book_library',
],
],
],
'driver' => [
'orm_default' => [
'class' => MappingDriverChain::class,
'drivers' => [
// ... and so on ...
config/autoload/doctrine.global.php
@asgrim
'doctrine' => [
'connection' => [
'orm_default' => [
'driver_class' => PDOPgSqlDriver::class,
'params' => [
'url' => 'postgres://user:pass@localhost/book_library',
],
],
],
'driver' => [
'orm_default' => [
'class' => MappingDriverChain::class,
'drivers' => [
// ... and so on ...
config/autoload/doctrine.global.php
@asgrim
<?php
declare(strict_types = 1);
use DoctrineORMEntityManagerInterface;
use DoctrineORMToolsConsoleHelperEntityManagerHelper;
use SymfonyComponentConsoleHelperHelperSet;
$container = require __DIR__ . '/container.php';
return new HelperSet([
'em' => new EntityManagerHelper(
$container->get(EntityManagerInterface::class)
),
]);
config/cli-config.php
@asgrim
Annotating the Entities
@asgrim
/**
* @ORMEntity
* @ORMTable(name="book")
*/
class Book
{
/**
* @ORMId
* @ORMColumn(name="id", type="guid")
* @ORMGeneratedValue(strategy="NONE")
* @var string
*/
private $id;
src/App/Entity/Book.php
@asgrim
/**
* @ORMEntity
* @ORMTable(name="book")
*/
class Book
{
/**
* @ORMId
* @ORMColumn(name="id", type="guid")
* @ORMGeneratedValue(strategy="NONE")
* @var string
*/
private $id;
src/App/Entity/Book.php
@asgrim
public function __invoke(UuidInterface $id): Book
{
/** @var Book|null $book */
$book = $this->repository->find((string)$id);
if (null === $book) {
throw ExceptionBookNotFound::fromUuid($id);
}
return $book;
}
src/App/Service/Book/DoctrineFindBookByUuid.php
@asgrim
try {
$this->entityManager->transactional(function () use ($book) {
$book->checkOut();
});
} catch (BookNotAvailable $bookNotAvailable) {
return new JsonResponse(['info' => $bookNotAvailable->getMessage()], 423);
}
src/App/Handler/CheckOutHandler.php
@asgrim
Generate the schema
$ vendor/bin/doctrine orm:schema-tool:create
ATTENTION: This operation should not be executed in a production
environment.
Creating database schema...
Database schema created successfully!
$
@asgrim
Insert some data
INSERT INTO book (id, name, in_stock) VALUES (
'1c06bec9-adae-47c2-b411-73b1db850e6f',
'The Great Escape',
true
);
@asgrim
/book/1c06bec9-adae-47c2-b411-.../check-out
{"info":"You have checked out The Great Escape"}
@asgrim
/book/1c06bec9-adae-47c2-b411-.../check-in
{"info":"You have checked in The Great Escape"}
@asgrim
Automatic Flushing Middleware
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
Add to pipeline
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
@asgrim
Doing more with
middleware
@asgrim
Authentication
@asgrim
public function process(
ServerRequestInterface $request,
RequestHandler $handler
) : Response {
$queryParams = $request->getQueryParams();
// DO NOT DO THIS IN REAL LIFE
// It's really not secure ;)
if (!array_key_exists('authenticated', $queryParams)
|| $queryParams['authenticated'] !== '1') {
return new JsonResponse(['error' => 'You are not authenticated.'], 403);
}
return $handler->handle($request);
}
Create the middleware
@asgrim
public function process(
ServerRequestInterface $request,
RequestHandler $handler
) : Response {
$queryParams = $request->getQueryParams();
// DO NOT DO THIS IN REAL LIFE
// It's really not secure ;)
if (!array_key_exists('authenticated', $queryParams)
|| $queryParams['authenticated'] !== '1') {
return new JsonResponse(['error' => 'You are not authenticated.'], 403);
}
return $handler->handle($request);
}
Create the middleware
@asgrim
public function process(
ServerRequestInterface $request,
RequestHandler $handler
) : Response {
$queryParams = $request->getQueryParams();
// DO NOT DO THIS IN REAL LIFE
// It's really not secure ;)
if (!array_key_exists('authenticated', $queryParams)
|| $queryParams['authenticated'] !== '1') {
return new JsonResponse(['error' => 'You are not authenticated.'], 403);
}
return $handler->handle($request);
}
Create the middleware
@asgrim
public function process(
ServerRequestInterface $request,
RequestHandler $handler
) : Response {
$queryParams = $request->getQueryParams();
// DO NOT DO THIS IN REAL LIFE
// It's really not secure ;)
if (!array_key_exists('authenticated', $queryParams)
|| $queryParams['authenticated'] !== '1') {
return new JsonResponse(['error' => 'You are not authenticated.'], 403);
}
return $handler->handle($request);
}
Create the middleware
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(AppMiddlewareAuthenticationMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Add middleware to pipe
@asgrim
Helios
composer require dasprid/helios
@asgrim
PSR7-Session
composer require psr7-sessions/storageless
@asgrim
public function __invoke(ContainerInterface $container, $_, array $_ = null)
{
$symmetricKey = 'do-not-store-this-key-in-git-store-it-in-configuration-instead-please';
$expirationTime = 1200; // 20 minutes
return new SessionMiddleware(
new SignerHmacSha256(),
$symmetricKey,
$symmetricKey,
SetCookie::create(SessionMiddleware::DEFAULT_COOKIE)
->withSecure(false) // false on purpose, unless you have https locally
->withHttpOnly(true)
->withPath('/'),
new Parser(),
$expirationTime,
new SystemClock()
);
}
Factory the middleware
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(PSR7SessionsStoragelessHttpSessionMiddleware::class);
$app->pipe(AppMiddlewareAuthenticationMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Add middleware to pipe
@asgrim
$session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
$session->set('counter', $session->get('counter', 0) + 1);
Session is stored in Request
@asgrim
$skills++;
@asgrim
To summarise...
● PSR-7 & PSR-15 lay the foundations!
● Diactoros is just a PSR-7 implementation
● Stratigility is a middleware pipeline: the main bit
● Expressive is a glue for everything
● ConfigProvider is great for aggregating (merging) config
● container-interop-doctrine makes Doctrine work easier
● Middleware all the things!
● Similar concepts in other frameworks (e.g. Slim)
@asgrim
Useful references
● https://github.com/asgrim/book-library
● https://framework.zend.com/blog/2017-12-14-expressive-3-dev.html
● https://framework.zend.com/blog/2017-03-13-expressive-2-migration.html
● https://framework.zend.com/blog/2017-03-30-expressive-config-routes.html
● https://mwop.net/blog/2016-05-16-programmatic-expressive.html
● https://docs.zendframework.com/zend-expressive/
Any questions?
James Titcumb
@asgrim

More Related Content

PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
PDF
Bringing modern PHP development to IBM i (ZendCon 2016)
PDF
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
PDF
Demystifying Object-Oriented Programming - ZendCon 2016
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHPNW2016)
PDF
Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Bringing modern PHP development to IBM i (ZendCon 2016)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Demystifying Object-Oriented Programming - ZendCon 2016
Kicking off with Zend Expressive and Doctrine ORM (PHPNW2016)
Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)

What's hot (19)

ODP
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
PDF
Conscious Decoupling - Lone Star PHP
PDF
Tame cloud complexity with F# powered DSLs (build stuff)
PPTX
Fasterflect
PDF
HTTP and Your Angry Dog
PPT
ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)
KEY
The use of Symfony2 @ Overblog
PPTX
Zephir - A Wind of Change for writing PHP extensions
ODP
Exploiting the newer perl to improve your plugins
ODP
Advanced Perl Techniques
PDF
PHP 8: What's New and Changed
PDF
Better rspec 進擊的 RSpec
ODP
Modern Perl
ODP
Pfm technical-inside
ODP
Modern Web Development with Perl
PDF
Supercharging WordPress Development in 2018
PDF
Ruby 2.4 Internals
KEY
Rack Middleware
PPTX
OroPlatform and OroCRM from a developer's perspective
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
Conscious Decoupling - Lone Star PHP
Tame cloud complexity with F# powered DSLs (build stuff)
Fasterflect
HTTP and Your Angry Dog
ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)
The use of Symfony2 @ Overblog
Zephir - A Wind of Change for writing PHP extensions
Exploiting the newer perl to improve your plugins
Advanced Perl Techniques
PHP 8: What's New and Changed
Better rspec 進擊的 RSpec
Modern Perl
Pfm technical-inside
Modern Web Development with Perl
Supercharging WordPress Development in 2018
Ruby 2.4 Internals
Rack Middleware
OroPlatform and OroCRM from a developer's perspective
Ad

Similar to Kicking off with Zend Expressive and Doctrine ORM (PHP South Africa 2018) (20)

PDF
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
PDF
Manage cloud infrastructures using Zend Framework 2 (and ZF1)
PDF
Creating a modern web application using Symfony API Platform, ReactJS and Red...
PDF
Ростислав Михайлив "Zend Framework 3 - evolution or revolution"
PPTX
Z ray plugins for dummies
PDF
A quick start on Zend Framework 2
PDF
What's New In Laravel 5
PDF
Plugin-based software design with Ruby and RubyGems
PDF
Decoupled Libraries for PHP
PDF
Proxy deep-dive java-one_20151027_001
PPT
Red5 - PHUG Workshops
PDF
ZF2 Presentation @PHP Tour 2011 in Lille
PPT
Zend Amf And Flex
PDF
Foundations of Zend Framework
PDF
Proxy Deep Dive Voxxed Belgrad 2015
PDF
ZFConf 2012: Zend Framework 2, a quick start (Enrico Zimuel)
PDF
Zend Framework 2 quick start
PPT
Secure Programming
PPT
Zend Framework
PDF
Workshop 17: EmberJS parte II
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Manage cloud infrastructures using Zend Framework 2 (and ZF1)
Creating a modern web application using Symfony API Platform, ReactJS and Red...
Ростислав Михайлив "Zend Framework 3 - evolution or revolution"
Z ray plugins for dummies
A quick start on Zend Framework 2
What's New In Laravel 5
Plugin-based software design with Ruby and RubyGems
Decoupled Libraries for PHP
Proxy deep-dive java-one_20151027_001
Red5 - PHUG Workshops
ZF2 Presentation @PHP Tour 2011 in Lille
Zend Amf And Flex
Foundations of Zend Framework
Proxy Deep Dive Voxxed Belgrad 2015
ZFConf 2012: Zend Framework 2, a quick start (Enrico Zimuel)
Zend Framework 2 quick start
Secure Programming
Zend Framework
Workshop 17: EmberJS parte II
Ad

More from James Titcumb (20)

PDF
Living the Best Life on a Legacy Project (phpday 2022).pdf
PDF
Tips for Tackling a Legacy Codebase (ScotlandPHP 2021)
PDF
Climbing the Abstract Syntax Tree (Midwest PHP 2020)
PDF
Best practices for crafting high quality PHP apps (Bulgaria 2019)
PDF
Climbing the Abstract Syntax Tree (php[world] 2019)
PDF
Best practices for crafting high quality PHP apps (php[world] 2019)
PDF
Crafting Quality PHP Applications (PHP Joburg Oct 2019)
PDF
Climbing the Abstract Syntax Tree (PHP Russia 2019)
PDF
Best practices for crafting high quality PHP apps - PHP UK 2019
PDF
Climbing the Abstract Syntax Tree (ScotlandPHP 2018)
PDF
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
PDF
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
PDF
Climbing the Abstract Syntax Tree (PHP Developer Days Dresden 2018)
PDF
Climbing the Abstract Syntax Tree (Southeast PHP 2018)
PDF
Crafting Quality PHP Applications (PHPkonf 2018)
PDF
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
PDF
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
PDF
Climbing the Abstract Syntax Tree (PHP UK 2018)
PDF
Crafting Quality PHP Applications (PHP Benelux 2018)
PDF
Crafting Quality PHP Applications (ConFoo YVR 2017)
Living the Best Life on a Legacy Project (phpday 2022).pdf
Tips for Tackling a Legacy Codebase (ScotlandPHP 2021)
Climbing the Abstract Syntax Tree (Midwest PHP 2020)
Best practices for crafting high quality PHP apps (Bulgaria 2019)
Climbing the Abstract Syntax Tree (php[world] 2019)
Best practices for crafting high quality PHP apps (php[world] 2019)
Crafting Quality PHP Applications (PHP Joburg Oct 2019)
Climbing the Abstract Syntax Tree (PHP Russia 2019)
Best practices for crafting high quality PHP apps - PHP UK 2019
Climbing the Abstract Syntax Tree (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
Climbing the Abstract Syntax Tree (PHP Developer Days Dresden 2018)
Climbing the Abstract Syntax Tree (Southeast PHP 2018)
Crafting Quality PHP Applications (PHPkonf 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Climbing the Abstract Syntax Tree (PHP UK 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (ConFoo YVR 2017)

Recently uploaded (20)

PPTX
Big Data Technologies - Introduction.pptx
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
Cloud computing and distributed systems.
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Electronic commerce courselecture one. Pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Approach and Philosophy of On baking technology
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
KodekX | Application Modernization Development
Big Data Technologies - Introduction.pptx
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Cloud computing and distributed systems.
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Dropbox Q2 2025 Financial Results & Investor Presentation
20250228 LYD VKU AI Blended-Learning.pptx
NewMind AI Monthly Chronicles - July 2025
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
MYSQL Presentation for SQL database connectivity
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Digital-Transformation-Roadmap-for-Companies.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Electronic commerce courselecture one. Pdf
Unlocking AI with Model Context Protocol (MCP)
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Approach and Philosophy of On baking technology
Network Security Unit 5.pdf for BCA BBA.
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
KodekX | Application Modernization Development

Kicking off with Zend Expressive and Doctrine ORM (PHP South Africa 2018)