DECOUPLE YOUR FRAMEWORK NOW,
THANK ME LATER
MICHELANGELO VAN DAM - @DRAGONBE
MICHELANGELO VAN DAM
CEO at in2it
Lead PHP architect
Community leader
Coach & mentor
FOSS contributor
Public speaker
FRAMEWORKS ARE GREAT!
• They abstract common tasks like email, database connectivity, routing
and a ton more…
• They allow us to quickly develop complex applications
• They offer good to great security and robustness
BUT…
• Frameworks have a nasty aftertaste when building business logic
• You need to use their database, log or cache adapter everywhere
• View templates are requiring framework components like
translations, escaping and other trivial purposes
• Best practices require to use modules, bundles or features to
separate business logic components
IMPROVEMENTS THROUGH STANDARDS
• The PHP-FIG standards motivates frameworks to use great
components to abstract functionality and ensure interoperability with
other frameworks and tools
• Major frameworks already offer this straight off the bat
• But add their own “secret sauce” to link it within their framework,
even when using Dependency Injection
WHAT ARE THE CHALLENGES?
UPGRADE (OR CHANGE) YOUR FRAMEWORK
UPGRADE TO THE LATEST PHP VERSION
BUSINESS LOGIC AND FRAMEWORKS MIXED
Framework X Business Logic
BL DB
FW DB
FW Logging
FW Mail
FW Service
BL Logging
BL Mail
BL Services
APPLYING INTERFACES IN BETWEEN
Framework X Business Logic
FW DB
FW
Logging
FW Mail
FW
Service
BL DB
BL
Logging
BL Mail
BL
Services
DB Interface
Logging Interface
Mail Interface
Service Interface
RESTAURANT PRINCIPLE
THE HOSTES
WILL BRING YOU TO YOUR
TABLE AND GIVES YOU THE
MENU AND WINE LIST.
A WAITER
WILL TAKE YOUR ORDER
FOR DRINKS AND FOOD
BARTENDER
WILL PREPARE YOUR
DRINKS
KITCHEN
WILL PREPARE YOUR MEAL
YOUR WAITER
BRINGS YOUR DRINKS
YOUR WAITER
WILL DELIVER YOUR FOOD
YOUR WAITER
WILL GIVE YOU THE BILL
WHEN DONE
YOUR WAITER
• Interfaces with the hostess to get started
• Interfaces with you to take your order
• Interfaces with the bar for drinks
• Interfaces with the cash register to present you a bill
YOUR WAITER
• Receives notification you have arrived at your table
• Receives your order from you
• Receives drinks from the bartender
• Receives food from the kitchen
• Receives money from you
HOW DO WE DO THIS IN CODE?
INTERFACES
DESIGN BY CONTRACTS
WHAT ARE INTERFACES
• Interfaces define a requirement without concrete implementation
• There’s no limit on interfaces implemented
• Everyone understands the “contract” immediately
• One interface per goal, feature or purpose
COMMON INTERFACES IN PHP
• Countable
• Iterator (and derivates)
• See language.oop5.interfaces on php.net for more details!
CUSTOM INTERFACES
interface TableGatewayInterface
{
    public function find(int $id): array;
    public function fetchRow(
        array $where = [], 
        array $order = []
    ): array;
    public function fetchAll(
        array $where = [], 
        array $order = [], 
        int $count = 0, 
        int $offset = 0
    ): array;
    public function insert(array $data): int;
    public function update(array $data, array $where = []): int;
}
BONUS: VERY TESTABLE!!!
• No need to implement concrete code, just use interfaces to guide
your development
• A class can implement multiple interfaces, testing can occur on a
single interface functionality at a time.
EVENTS
WHAT ARE EVENTS?
• Events allow us to run tasks in the background and call back when
completed.
• Implements the observer pattern (e.g. SplSubject & SplObserver)
• One observer can have many subscribers
• A subscriber can subscribe to many observer objects
EXAMPLE
$memberService = new MemberService();
$memberService->attach(new DBObserver());
$memberService->attach(new LogObserver());
$memberService->attach(new EmailObserver());
$memberService->attach(new CacheObserver());
$memberService->attach(new SearchObserver());
$memberService->register(new Member('John', 'Doe', 'j.doe@example.com'));
EVENT OBSERVATION
Register
DB
Log
Email
Cache
Search
Storing in DB
Logging
Sending email
Write to cache
Update indexes
EVENT OBSERVATION AFTER EVENTS
Register
DB
Log
Email
Cache
Search
Storing in DB
Logging
Sending email
Write to cache
Update indexes
BENEFITS
• Reusable logic
• Runs in the background, so no delays
• Scalable
GLUING ALL TOGETHER
Core Business
Logic
Silex Web
Frontend
Apigility
API
Phalcon Web
Backend
Core Business
Logic
Slim Web
Frontend
Zend
Expressive
API
Python Web
Backend
GLUE CLASS TO INTERACT 1/3
class SilexServiceProvider implements ServiceProviderInterface
{
    public function register(Container $container)
    {
        $dsn = $container['cb_config_db.dsn'] ?: '';
        $username = $container['cb_config_db.username'] ?: '';
        $password = $container['cb_config_db.password'] ?: '';
        $pdo = new PDO($dsn, $username, $password);
        $authorTable = new AuthorTable($pdo);
        $authorHydrator = new AuthorHydrator();
        $author = new Author();
        $bookTable = new BookTable($pdo);
        $bookHydrator = new BookHydrator();
        $book = new Book();
        
        $memberTable = new MemberTable($pdo);
        $memberHydrator = new MemberHydrator();
        $member = new Member();
GLUE CLASS TO INTERACT 2/3
        $serviceLocator = new ServiceLocator();
        $serviceLocator
            ->set('CloudbooksAuthorModelAuthorTable', $authorTable)
            ->set('CloudbooksAuthorModelAuthorHydrator', $authorHydrator)
            ->set('CloudbooksAuthorEntityAuthor', $author)
            ->set('CloudbooksBookModelBookTable', $bookTable)
            ->set('CloudbooksBookModelBookHydrator', $bookHydrator)
            ->set('CloudbooksBookEntityBook', $book)
            ->set('CloudbooksMemberModelMemberTable', $memberTable)
            ->set('CloudbooksMemberModelMemberHydrator', $memberHydrator)
            ->set('CloudbooksMemberEntityMember', $member);
    }
GLUE CLASS TO INTERACT 3/3
        $container['cb_author_service'] = $container->factory(function () use ($serviceLocator) {
            $serviceFactory = new AuthorServiceFactory();
            return $serviceFactory->createService($serviceLocator);
        });
        $container['cb_book_service'] = $container->factory(function () use ($serviceLocator) {
            $serviceFactory = new BookServiceFactory();
            return $serviceFactory->createService($serviceLocator);
        });
        $container['cb_member_service'] = $container->factory(function () use ($serviceLocator) {
            $serviceFactory = new MemberServiceFactory();
            return $serviceFactory->createService($serviceLocator);
        });
    }
LET’S RECAP
https://github.com/sensiolabs-de/deptrac
THANK YOU!

Decouple your framework now, thank me later