SlideShare a Scribd company logo
1 of 97
Download to read offline
Decoupling with 
Design Patterns 
and Symfony DIC
@everzet 
· Spent more than 7 
years writing so!ware 
· Spent more than 4 
years learning 
businesses 
· Now filling the gaps 
between the two as a 
BDD Practice Manager 
@Inviqa
behat 3 
promise #1 (of 2): 
extensibility
“Extensibility is a so!ware design 
principle defined as a system’s ability 
to have new functionality extended, in 
which the system’s internal structure 
and data flow are minimally or not 
affected”
“So!ware entities (classes, modules, 
functions, etc.) should be open for 
extension, but closed for modification”
behat 3 
promise #2 (of 2): 
backwards compatibility
behat 3 
- extensibility as the core concept 
- BC through extensibility
Symfony Bundles 
Behat extensions
Symfony Bundles & Behat extensions 
1. Framework creates a temporary 
container 
2. Framework asks the bundle to add its 
services 
3. Framework merges all temporary 
containers 
4. Framework compiles merged 
container
interface CompilerPassInterface 
{ 
/** 
* You can modify the container here before it is dumped to PHP code. 
* 
* @param ContainerBuilder $container 
* 
* @api 
*/ 
public function process(ContainerBuilder $container); 
}
class YourSuperBundle extends Bundle 
{ 
public function build(ContainerBuilder $container) 
{ 
parent::build($container); 
$container->addCompilerPass(new YourCompilerPass()); 
} 
}
v3.0 v1.0 
(extensibility solution v1)
challenge: 
behat as the most extensible 
test framework
pattern: observer
class HookDispatcher extends DispatchingService implements EventSubscriberInterface 
{ 
public static function getSubscribedEvents() 
{ 
return array( 
EventInterface::BEFORE_SUITE => array('dispatchHooks', 10), 
EventInterface::AFTER_SUITE => array('dispatchHooks', 10), 
EventInterface::BEFORE_FEATURE => array('dispatchHooks', 10), 
... 
); 
} 
public function dispatchHooks(LifecycleEventInterface $event) 
{ 
$hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); 
$this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); 
foreach ($hooksProvider->getHooksForEvent($event) as $hook) { 
$this->dispatchHook($hook, $event); 
} 
} 
... 
}
class HooksCarrierEvent extends Event implements LifecycleEventInterface 
{ 
public function addHook(HookInterface $hook) 
{ 
$this->hooks[] = $hook; 
} 
public function getHooksForEvent(Event $event) 
{ 
return array_filter( 
$this->hooks, 
function ($hook) use ($event) { 
$eventName = $event->getName(); 
if ($eventName !== $hook->getEventName()) { 
return false; 
} 
return $hook; 
} 
); 
} 
... 
}
class DictionaryReader implements EventSubscriberInterface 
{ 
public static function getSubscribedEvents() 
{ 
return array( 
EventInterface::LOAD_HOOKS => array('loadHooks', 0), 
... 
); 
} 
public function loadHooks(HooksCarrierEvent $event) 
{ 
foreach ($this->read($event->getSuite(), $event->getContextPool()) as $callback) { 
if ($callback instanceof HookInterface) { 
$event->addHook($callback); 
} 
} 
} 
... 
}
extension point
<container xmlns="http://symfony.com/schema/dic/services" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="..."> 
<services> 
<service id="event_dispatcher" 
class="SymfonyComponentEventDispatcherEventDispatcher"/> 
<service id="hook.hook_dispatcher" 
class="BehatBehatHookEventSubscriberHookDispatcher"> 
<argument type="service" id="event_dispatcher"/> 
<tag name="event_subscriber"/> 
</service> 
<service id="context.dictionary_reader" 
class="BehatBehatContextEventSubscriberDictionaryReader"> 
<tag name="event_subscriber"/> 
</service> 
</services> 
</container>
class EventSubscribersPass implements CompilerPassInterface 
{ 
public function process(ContainerBuilder $container) 
{ 
$dispatcherDefinition = $container->getDefinition('event_dispatcher'); 
foreach ($container->findTaggedServiceIds('event_subscriber') as $id => $attributes) { 
$dispatcherDefinition->addMethodCall('addSubscriber', array(new Reference($id))); 
} 
} 
}
where event dispatcher / 
observer is useful?
pub/sub as an 
architectural choice
“Coupling is a degree to which each 
program module relies on each one of 
the other modules”
“Cohesion is a degree to which the 
elements of a module belong together”
“Coupling is a degree to which each 
program module relies on each one of 
the other modules” 
public function dispatchHooks(LifecycleEventInterface $event) 
{ 
$hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); 
$this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); 
foreach ($hooksProvider->getHooksForEvent($event) as $hook) { 
$this->dispatchHook($hook, $event); 
} 
}
“Cohesion is a degree to which the 
elements of a module belong together” 
public function dispatchHooks(LifecycleEventInterface $event) 
{ 
$hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); 
$this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); 
foreach ($hooksProvider->getHooksForEvent($event) as $hook) { 
$this->dispatchHook($hook, $event); 
} 
}
Coupling ↓ 
Cohesion ↑
scratch that
v3.0 v2.0 
(extensibility solution v2)
There is no single solution for 
extensibility. Because extensibility is 
not a single problem
framework extensions 
Since v2.5 behat has some very 
important extensions: 
1. MinkExtension 
2. Symfony2Extension
problem: 
there are multiple possible 
algorithms for a single 
responsibility
pattern: delegation loop
final class EnvironmentManager 
{ 
private $handlers = array(); 
public function registerEnvironmentHandler(EnvironmentHandler $handler) 
{ 
$this->handlers[] = $handler; 
} 
public function buildEnvironment(Suite $suite) 
{ 
foreach ($this->handlers as $handler) { 
... 
} 
} 
public function isolateEnvironment(Environment $environment, $testSubject = null) 
{ 
foreach ($this->handlers as $handler) { 
... 
} 
} 
}
interface EnvironmentHandler 
{ 
public function supportsSuite(Suite $suite); 
public function buildEnvironment(Suite $suite); 
public function supportsEnvironmentAndSubject(Environment $environment, $testSubject = null); 
public function isolateEnvironment(Environment $environment, $testSubject = null); 
}
extension point
<container xmlns="http://symfony.com/schema/dic/services" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="..."> 
<services> 
<service id=“environment.manager” 
class="BehatTestworkEnvironmentEnvironmentManager” /> 
<service id=“behat.context.environment.handler” 
class=“BehatBehatContextEnvironmentContextEnvironmentHandler”> 
<tag name=“environment.handler”/> 
</service> 
</services> 
</container>
final class EnvironmentHandlerPass implements CompilerPassInterface 
{ 
public function process(ContainerBuilder $container) 
{ 
$references = $this->processor->findAndSortTaggedServices($container, ‘environment.handler’); 
$definition = $container->getDefinition(‘environment.manager’); 
foreach ($references as $reference) { 
$definition->addMethodCall('registerEnvironmentHandler', array($reference)); 
} 
} 
}
where delegation loop is 
useful?
behat testers 
There are 5 testers in behat core: 
1. FeatureTester 
2. ScenarioTester 
3. OutlineTester 
4. BackgroundTester 
5. StepTester
behat testers 
Behat needs to provide you with: 
· Hooks 
· Events
problem: 
we need to dynamically extend 
the core testers behaviour
pattern: decorator
final class RuntimeScenarioTester implements ScenarioTester 
{ 
public function setUp(Environment $env, FeatureNode $feature, 
Scenario $example, $skip) 
{ 
return new SuccessfulSetup(); 
} 
public function test(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip = false) 
{ 
... 
} 
public function tearDown(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip, TestResult $result) 
{ 
return new SuccessfulTeardown(); 
} 
}
interface ScenarioTester 
{ 
public function setUp(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip); 
public function test(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip); 
public function tearDown(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip, TestResult $result); 
}
final class EventDispatchingScenarioTester implements ScenarioTester 
{ 
public function __construct(ScenarioTester $baseTester, EventDispatcherInterface $eventDispatcher) 
{ 
$this->baseTester = $baseTester; 
$this->eventDispatcher = $eventDispatcher; 
} 
public function setUp(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip) 
{ 
$event = new BeforeScenarioTested($env, $feature, $scenario); 
$this->eventDispatcher->dispatch($this->beforeEventName, $event); 
$setup = $this->baseTester->setUp($env, $feature, $scenario, $skip); 
return $setup; 
} 
public function test(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip) 
{ 
return $this->baseTester->test($env, $feature, $scenario, $skip); 
} 
public function tearDown(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip, TestResult $result) 
{ 
$teardown = $this->baseTester->tearDown($env, $feature, $scenario, $skip, $result); 
$event = new AfterScenarioTested($env, $feature, $scenario, $result, $teardown); 
$this->eventDispatcher->dispatch($event); 
return $teardown; 
} 
}
final class HookableScenarioTester implements ScenarioTester 
{ 
public function __construct(ScenarioTester $baseTester, HookDispatcher $hookDispatcher) 
{ 
$this->baseTester = $baseTester; 
$this->hookDispatcher = $hookDispatcher; 
} 
public function setUp(Environment $env, FeatureNode $feature, 
Scenario $example, $skip) 
{ 
$setup = $this->baseTester->setUp($env, $feature, $scenario, $skip); 
$hookCallResults = $this->hookDispatcher->dispatchScopeHooks($setup); 
return new HookedSetup($setup, $hookCallResults); 
} 
public function test(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip = false) 
{ 
return $this->baseTester->test($env, $feature, $scenario, $skip); 
} 
public function tearDown(Environment $env, FeatureNode $feature, 
Scenario $scenario, $skip, TestResult $result) 
{ 
$teardown = $this->baseTester->tearDown($env, $feature, $scenario, $skip, $result); 
$hookCallResults = $this->hookDispatcher->dispatchScopeHooks($teardown); 
return new HookedTeardown($teardown, $hookCallResults); 
} 
}
extension point
<container xmlns="http://symfony.com/schema/dic/services" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="..."> 
<services> 
<service id=“tester.scenario” 
class="BehatBehatTesterScenarioTester” /> 
<service id=“hooks.tester.scenario” 
class=“BehatBehatHooksTesterScenarioTester”> 
... 
<tag name=“tester.scenario_wrapper” order=“100”/> 
</service> 
<service id=“events.tester.scenario” 
class=“BehatBehatEventsTesterScenarioTester”> 
... 
<tag name=“tester.scenario_wrapper” order=“200”/> 
</service> 
</services> 
</container>
final class ScenarioTesterWrappersPass implements CompilerPassInterface 
{ 
public function process(ContainerBuilder $container) 
{ 
$references = $this->findAndReorderTaggedServices($container, ‘tester.scenario_wrapper’); 
foreach ($references as $reference) { 
$id = (string) $reference; 
$renamedId = $id . '.inner'; 
// This logic is based on SymfonyComponentDependencyInjectionCompilerDecoratorServicePass 
$definition = $container->getDefinition(‘tester.scenario’); 
$container->setDefinition($renamedId, $definition); 
$container->setAlias('tester.scenario', new Alias($id, $public)); 
$wrappingService = $container->getDefinition($id); 
$wrappingService->replaceArgument(0, new Reference($renamedId)); 
} 
} 
... 
}
where decorator is useful?
behat output 
Behat has a very simple output:
behat output 
Until you start using backgrounds:
behat output 
And throwing exceptions from their 
hooks:
problem: 
we need to add behaviour to 
complex output logic
pattern: observer
pattern: chain of responsibility
pattern: composite
final class NodeEventListeningFormatter implements Formatter 
{ 
public function __construct(EventListener $listener) 
{ 
$this->listener = $listener; 
} 
public static function getSubscribedEvents() 
{ 
return array(TestworkEventDispatcher::BEFORE_ALL_EVENTS => 'listenEvent'); 
} 
public function listenEvent(Event $event, $eventName = null) 
{ 
$eventName = $eventName ?: $event->getName(); 
$this->listener->listenEvent($this, $event, $eventName); 
} 
}
final class ChainEventListener implements EventListener, Countable, IteratorAggregate 
{ 
private $listeners; 
public function __construct(array $listeners) 
{ 
$this->listeners = $listeners; 
} 
public function listenEvent(Formatter $formatter, Event $event, $eventName) 
{ 
foreach ($this->listeners as $listener) { 
$listener->listenEvent($formatter, $event, $eventName); 
} 
} 
... 
}
Event listeners 
Behat has 2 types of listeners: 
1. Printers 
2. Flow controllers
final class StepListener implements EventListener 
{ 
public function listenEvent(Formatter $formatter, Event $event, $eventName) 
{ 
$this->captureScenarioOnScenarioEvent($event); 
$this->forgetScenarioOnAfterEvent($eventName); 
$this->printStepSetupOnBeforeEvent($formatter, $event); 
$this->printStepOnAfterEvent($formatter, $event); 
} 
... 
}
How do backgrounds work?
class FirstBackgroundFiresFirstListener implements EventListener 
{ 
public function __construct(EventListener $descendant) 
{ 
$this->descendant = $descendant; 
} 
public function listenEvent(Formatter $formatter, Event $event, $eventName) 
{ 
$this->flushStatesIfBeginningOfTheFeature($eventName); 
$this->markFirstBackgroundPrintedAfterBackground($eventName); 
if ($this->isEventDelayedUntilFirstBackgroundPrinted($event)) { 
$this->delayedUntilBackgroundEnd[] = array($event, $eventName); 
return; 
} 
$this->descendant->listenEvent($formatter, $event, $eventName); 
$this->fireDelayedEventsOnAfterBackground($formatter, $eventName); 
} 
}
where composite and CoR 
are useful?
interface StepTester 
{ 
public function setUp(Environment $env, FeatureNode $feature, 
StepNode $step, $skip); 
public function test(Environment $env, FeatureNode $feature, 
StepNode $step, $skip); 
public function tearDown(Environment $env, FeatureNode $feature, 
StepNode $step, $skip, StepResult $result); 
}
problem: 
we need to introduce 
backwards incompatible 
change into the API
pattern: adapter
interface ScenarioStepTester 
{ 
public function setUp(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip); 
public function test(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip); 
public function tearDown(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip, 
StepResult $result); 
}
final class StepToScenarioTesterAdapter implements ScenarioStepTester 
{ 
public function __construct(StepTester $stepTester) { ... } 
public function setUp(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip) 
{ 
return $this->stepTester->setUp($env, $feature, $step, $skip); 
} 
public function test(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip) 
{ 
return $this->stepTester->test($env, $feature, $step, $skip); 
} 
public function tearDown(Environment $env, FeatureNode $feature, 
ScenarioNode $scenario, StepNode $step, $skip, 
StepResult $result) 
{ 
return $this->stepTester-> tearDown($env, $feature, $step, $skip); 
} 
}
final class StepTesterAdapterPass implements CompilerPassInterface 
{ 
public function process(ContainerBuilder $container) 
{ 
$references = $this->processor->findAndSortTaggedServices($container, ‘tester.step_wrapper’); 
foreach ($references as $reference) { 
$id = (string) $reference; 
$renamedId = $id . ‘.adaptee’; 
$adapteeDefinition = $container->getDefinition($id); 
$reflection = new ReflectionClass($adapteeDefinition->getClass()); 
if (!$reflection->implementsInterface(‘StepTester’)) { 
return; 
} 
$container->removeDefinition($id); 
$container->setDefinition( 
$id, 
new Definition(‘StepToScenarioTesterAdapter’, array( 
$adapteeDefinition 
)); 
); 
} 
} 
}
where adapter is useful?
demo
backwards 
compatibility
backwards compatibility 
Backwards compatibility in Behat 
comes from the extensibility. 
1. Everything is extension 
2. New features are extensions too 
3. New features could be toggled on/off
performance 
implications
performance implications 
· 2x more objects in v3 than in v2 
· Value objects are used instead of 
simple types 
· A lot of additional concepts 
throughout 
· It must be slow
yet...
how?
how? 
immutability!
TestWork
TestWork
how?
Step1: Close the doors 
Assume you have no extension points 
by default. 
1. Private properties 
2. Final classes
Step 2: Open doors properly when you need them 
1. Identify the need for extension points 
2. Make extension points explicit
Private properties 
...
Final classes
class BundleFeatureLocator extends FilesystemFeatureLocator 
{ 
public function locateSpecifications(Suite $suite, $locator) 
{ 
if (!$suite instanceof SymfonyBundleSuite) { 
return new noSpecificationsIterator($suite); 
} 
$bundle = $suite->getBundle(); 
if (0 !== strpos($locator, '@' . $bundle->getName())) { 
return new NoSpecificationsIterator($suite); 
} 
$locatorSuffix = substr($locator, strlen($bundle->getName()) + 1); 
return parent::locateSpecifications($suite, $bundle->getPath() . '/Features' . $locatorSuffix); 
} 
}
final class BundleFeatureLocator implements SpecificationLocator 
{ 
public function __construct(SpecificationLocator $baseLocator) { ... } 
public function locateSpecifications(Suite $suite, $locator) 
{ 
if (!$suite instanceof SymfonyBundleSuite) { 
return new noSpecificationsIterator($suite); 
} 
$bundle = $suite->getBundle(); 
if (0 !== strpos($locator, '@' . $bundle->getName())) { 
return new NoSpecificationsIterator($suite); 
} 
$locatorSuffix = substr($locator, strlen($bundle->getName()) + 1); 
return $this->baseLocator->locateSpecifications($suite, $bundle->getPath() . '/Features' . $locatorSuffix); 
} 
}
the most closed most 
extensible testing 
framework
ask questions 
close Feed! L♻♻ps: 
https://joind.in/11559

More Related Content

What's hot

Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationSamuel ROZE
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkG Woo
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolveXSolve
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingSamuel ROZE
 

What's hot (20)

Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php framework
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Mocking Demystified
Mocking DemystifiedMocking Demystified
Mocking Demystified
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 

Viewers also liked

Design pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesDesign pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesGiulio De Donato
 
Design patterns avec Symfony
Design patterns avec SymfonyDesign patterns avec Symfony
Design patterns avec SymfonyMohammed Rhamnia
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)Konstantin Kudryashov
 
Understanding craftsmanship
Understanding craftsmanshipUnderstanding craftsmanship
Understanding craftsmanshipMarcello Duarte
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projectsKonstantin Kudryashov
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsKonstantin Kudryashov
 
Data Flow Patterns in Angular 2 - Sebastian Müller
Data Flow Patterns in Angular 2 -  Sebastian MüllerData Flow Patterns in Angular 2 -  Sebastian Müller
Data Flow Patterns in Angular 2 - Sebastian MüllerSebastian Holstein
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Konstantin Kudryashov
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Fabien Potencier
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)James Titcumb
 
Symfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiquesSymfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiquesNoel GUILBERT
 

Viewers also liked (20)

Design pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesDesign pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentes
 
Design patterns avec Symfony
Design patterns avec SymfonyDesign patterns avec Symfony
Design patterns avec Symfony
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
Modern Agile Project Toolbox
Modern Agile Project ToolboxModern Agile Project Toolbox
Modern Agile Project Toolbox
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)
 
Taking back BDD
Taking back BDDTaking back BDD
Taking back BDD
 
Understanding craftsmanship
Understanding craftsmanshipUnderstanding craftsmanship
Understanding craftsmanship
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
BDD by example
BDD by exampleBDD by example
BDD by example
 
Modern Project Toolbox
Modern Project ToolboxModern Project Toolbox
Modern Project Toolbox
 
Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projects
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projects
 
BDD в PHP с Behat и Mink
BDD в PHP с Behat и MinkBDD в PHP с Behat и Mink
BDD в PHP с Behat и Mink
 
BDD для PHP проектов
BDD для PHP проектовBDD для PHP проектов
BDD для PHP проектов
 
Data Flow Patterns in Angular 2 - Sebastian Müller
Data Flow Patterns in Angular 2 -  Sebastian MüllerData Flow Patterns in Angular 2 -  Sebastian Müller
Data Flow Patterns in Angular 2 - Sebastian Müller
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
 
Symfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiquesSymfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiques
 

Similar to Decoupling with Design Patterns and Symfony2 DIC

PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodeSWIFTotter Solutions
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmersAlexander Varwijk
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Hugo Hamon
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...go_oh
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenchesLukas Smith
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsMichael Peacock
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
Domain Driven Design using Laravel
Domain Driven Design using LaravelDomain Driven Design using Laravel
Domain Driven Design using Laravelwajrcs
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Jakub Zalas
 
Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Jason Lotito
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 3camp
 
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014Matthias Noback
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014Matthias Noback
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkIndicThreads
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Frameworkvhazrati
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 

Similar to Decoupling with Design Patterns and Symfony2 DIC (20)

PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better Code
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
Domain Driven Design using Laravel
Domain Driven Design using LaravelDomain Driven Design using Laravel
Domain Driven Design using Laravel
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2
 
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014
 
Overview Of Lift Framework
Overview Of Lift FrameworkOverview Of Lift Framework
Overview Of Lift Framework
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web Framework
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Framework
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 

Recently uploaded

Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonApplitools
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profileakrivarotava
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxAndreas Kunz
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogueitservices996
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesKrzysztofKkol1
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...Akihiro Suda
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slidesvaideheekore1
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 

Recently uploaded (20)

Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profile
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogue
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slides
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 

Decoupling with Design Patterns and Symfony2 DIC

  • 1. Decoupling with Design Patterns and Symfony DIC
  • 2. @everzet · Spent more than 7 years writing so!ware · Spent more than 4 years learning businesses · Now filling the gaps between the two as a BDD Practice Manager @Inviqa
  • 3.
  • 4.
  • 5.
  • 6. behat 3 promise #1 (of 2): extensibility
  • 7. “Extensibility is a so!ware design principle defined as a system’s ability to have new functionality extended, in which the system’s internal structure and data flow are minimally or not affected”
  • 8. “So!ware entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”
  • 9. behat 3 promise #2 (of 2): backwards compatibility
  • 10.
  • 11. behat 3 - extensibility as the core concept - BC through extensibility
  • 12. Symfony Bundles Behat extensions
  • 13. Symfony Bundles & Behat extensions 1. Framework creates a temporary container 2. Framework asks the bundle to add its services 3. Framework merges all temporary containers 4. Framework compiles merged container
  • 14. interface CompilerPassInterface { /** * You can modify the container here before it is dumped to PHP code. * * @param ContainerBuilder $container * * @api */ public function process(ContainerBuilder $container); }
  • 15. class YourSuperBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass(new YourCompilerPass()); } }
  • 16. v3.0 v1.0 (extensibility solution v1)
  • 17. challenge: behat as the most extensible test framework
  • 19. class HookDispatcher extends DispatchingService implements EventSubscriberInterface { public static function getSubscribedEvents() { return array( EventInterface::BEFORE_SUITE => array('dispatchHooks', 10), EventInterface::AFTER_SUITE => array('dispatchHooks', 10), EventInterface::BEFORE_FEATURE => array('dispatchHooks', 10), ... ); } public function dispatchHooks(LifecycleEventInterface $event) { $hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); $this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); foreach ($hooksProvider->getHooksForEvent($event) as $hook) { $this->dispatchHook($hook, $event); } } ... }
  • 20. class HooksCarrierEvent extends Event implements LifecycleEventInterface { public function addHook(HookInterface $hook) { $this->hooks[] = $hook; } public function getHooksForEvent(Event $event) { return array_filter( $this->hooks, function ($hook) use ($event) { $eventName = $event->getName(); if ($eventName !== $hook->getEventName()) { return false; } return $hook; } ); } ... }
  • 21. class DictionaryReader implements EventSubscriberInterface { public static function getSubscribedEvents() { return array( EventInterface::LOAD_HOOKS => array('loadHooks', 0), ... ); } public function loadHooks(HooksCarrierEvent $event) { foreach ($this->read($event->getSuite(), $event->getContextPool()) as $callback) { if ($callback instanceof HookInterface) { $event->addHook($callback); } } } ... }
  • 23. <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..."> <services> <service id="event_dispatcher" class="SymfonyComponentEventDispatcherEventDispatcher"/> <service id="hook.hook_dispatcher" class="BehatBehatHookEventSubscriberHookDispatcher"> <argument type="service" id="event_dispatcher"/> <tag name="event_subscriber"/> </service> <service id="context.dictionary_reader" class="BehatBehatContextEventSubscriberDictionaryReader"> <tag name="event_subscriber"/> </service> </services> </container>
  • 24. class EventSubscribersPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $dispatcherDefinition = $container->getDefinition('event_dispatcher'); foreach ($container->findTaggedServiceIds('event_subscriber') as $id => $attributes) { $dispatcherDefinition->addMethodCall('addSubscriber', array(new Reference($id))); } } }
  • 25. where event dispatcher / observer is useful?
  • 26. pub/sub as an architectural choice
  • 27. “Coupling is a degree to which each program module relies on each one of the other modules”
  • 28. “Cohesion is a degree to which the elements of a module belong together”
  • 29. “Coupling is a degree to which each program module relies on each one of the other modules” public function dispatchHooks(LifecycleEventInterface $event) { $hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); $this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); foreach ($hooksProvider->getHooksForEvent($event) as $hook) { $this->dispatchHook($hook, $event); } }
  • 30. “Cohesion is a degree to which the elements of a module belong together” public function dispatchHooks(LifecycleEventInterface $event) { $hooksProvider = new HooksCarrierEvent($event->getSuite(), $event->getContextPool()); $this->dispatch(EventInterface::LOAD_HOOKS, $hooksProvider); foreach ($hooksProvider->getHooksForEvent($event) as $hook) { $this->dispatchHook($hook, $event); } }
  • 33. v3.0 v2.0 (extensibility solution v2)
  • 34. There is no single solution for extensibility. Because extensibility is not a single problem
  • 35. framework extensions Since v2.5 behat has some very important extensions: 1. MinkExtension 2. Symfony2Extension
  • 36. problem: there are multiple possible algorithms for a single responsibility
  • 38. final class EnvironmentManager { private $handlers = array(); public function registerEnvironmentHandler(EnvironmentHandler $handler) { $this->handlers[] = $handler; } public function buildEnvironment(Suite $suite) { foreach ($this->handlers as $handler) { ... } } public function isolateEnvironment(Environment $environment, $testSubject = null) { foreach ($this->handlers as $handler) { ... } } }
  • 39. interface EnvironmentHandler { public function supportsSuite(Suite $suite); public function buildEnvironment(Suite $suite); public function supportsEnvironmentAndSubject(Environment $environment, $testSubject = null); public function isolateEnvironment(Environment $environment, $testSubject = null); }
  • 41. <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..."> <services> <service id=“environment.manager” class="BehatTestworkEnvironmentEnvironmentManager” /> <service id=“behat.context.environment.handler” class=“BehatBehatContextEnvironmentContextEnvironmentHandler”> <tag name=“environment.handler”/> </service> </services> </container>
  • 42. final class EnvironmentHandlerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, ‘environment.handler’); $definition = $container->getDefinition(‘environment.manager’); foreach ($references as $reference) { $definition->addMethodCall('registerEnvironmentHandler', array($reference)); } } }
  • 43. where delegation loop is useful?
  • 44. behat testers There are 5 testers in behat core: 1. FeatureTester 2. ScenarioTester 3. OutlineTester 4. BackgroundTester 5. StepTester
  • 45. behat testers Behat needs to provide you with: · Hooks · Events
  • 46. problem: we need to dynamically extend the core testers behaviour
  • 48. final class RuntimeScenarioTester implements ScenarioTester { public function setUp(Environment $env, FeatureNode $feature, Scenario $example, $skip) { return new SuccessfulSetup(); } public function test(Environment $env, FeatureNode $feature, Scenario $scenario, $skip = false) { ... } public function tearDown(Environment $env, FeatureNode $feature, Scenario $scenario, $skip, TestResult $result) { return new SuccessfulTeardown(); } }
  • 49. interface ScenarioTester { public function setUp(Environment $env, FeatureNode $feature, Scenario $scenario, $skip); public function test(Environment $env, FeatureNode $feature, Scenario $scenario, $skip); public function tearDown(Environment $env, FeatureNode $feature, Scenario $scenario, $skip, TestResult $result); }
  • 50. final class EventDispatchingScenarioTester implements ScenarioTester { public function __construct(ScenarioTester $baseTester, EventDispatcherInterface $eventDispatcher) { $this->baseTester = $baseTester; $this->eventDispatcher = $eventDispatcher; } public function setUp(Environment $env, FeatureNode $feature, Scenario $scenario, $skip) { $event = new BeforeScenarioTested($env, $feature, $scenario); $this->eventDispatcher->dispatch($this->beforeEventName, $event); $setup = $this->baseTester->setUp($env, $feature, $scenario, $skip); return $setup; } public function test(Environment $env, FeatureNode $feature, Scenario $scenario, $skip) { return $this->baseTester->test($env, $feature, $scenario, $skip); } public function tearDown(Environment $env, FeatureNode $feature, Scenario $scenario, $skip, TestResult $result) { $teardown = $this->baseTester->tearDown($env, $feature, $scenario, $skip, $result); $event = new AfterScenarioTested($env, $feature, $scenario, $result, $teardown); $this->eventDispatcher->dispatch($event); return $teardown; } }
  • 51. final class HookableScenarioTester implements ScenarioTester { public function __construct(ScenarioTester $baseTester, HookDispatcher $hookDispatcher) { $this->baseTester = $baseTester; $this->hookDispatcher = $hookDispatcher; } public function setUp(Environment $env, FeatureNode $feature, Scenario $example, $skip) { $setup = $this->baseTester->setUp($env, $feature, $scenario, $skip); $hookCallResults = $this->hookDispatcher->dispatchScopeHooks($setup); return new HookedSetup($setup, $hookCallResults); } public function test(Environment $env, FeatureNode $feature, Scenario $scenario, $skip = false) { return $this->baseTester->test($env, $feature, $scenario, $skip); } public function tearDown(Environment $env, FeatureNode $feature, Scenario $scenario, $skip, TestResult $result) { $teardown = $this->baseTester->tearDown($env, $feature, $scenario, $skip, $result); $hookCallResults = $this->hookDispatcher->dispatchScopeHooks($teardown); return new HookedTeardown($teardown, $hookCallResults); } }
  • 53. <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..."> <services> <service id=“tester.scenario” class="BehatBehatTesterScenarioTester” /> <service id=“hooks.tester.scenario” class=“BehatBehatHooksTesterScenarioTester”> ... <tag name=“tester.scenario_wrapper” order=“100”/> </service> <service id=“events.tester.scenario” class=“BehatBehatEventsTesterScenarioTester”> ... <tag name=“tester.scenario_wrapper” order=“200”/> </service> </services> </container>
  • 54. final class ScenarioTesterWrappersPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $references = $this->findAndReorderTaggedServices($container, ‘tester.scenario_wrapper’); foreach ($references as $reference) { $id = (string) $reference; $renamedId = $id . '.inner'; // This logic is based on SymfonyComponentDependencyInjectionCompilerDecoratorServicePass $definition = $container->getDefinition(‘tester.scenario’); $container->setDefinition($renamedId, $definition); $container->setAlias('tester.scenario', new Alias($id, $public)); $wrappingService = $container->getDefinition($id); $wrappingService->replaceArgument(0, new Reference($renamedId)); } } ... }
  • 56. behat output Behat has a very simple output:
  • 57. behat output Until you start using backgrounds:
  • 58. behat output And throwing exceptions from their hooks:
  • 59. problem: we need to add behaviour to complex output logic
  • 61. pattern: chain of responsibility
  • 63. final class NodeEventListeningFormatter implements Formatter { public function __construct(EventListener $listener) { $this->listener = $listener; } public static function getSubscribedEvents() { return array(TestworkEventDispatcher::BEFORE_ALL_EVENTS => 'listenEvent'); } public function listenEvent(Event $event, $eventName = null) { $eventName = $eventName ?: $event->getName(); $this->listener->listenEvent($this, $event, $eventName); } }
  • 64. final class ChainEventListener implements EventListener, Countable, IteratorAggregate { private $listeners; public function __construct(array $listeners) { $this->listeners = $listeners; } public function listenEvent(Formatter $formatter, Event $event, $eventName) { foreach ($this->listeners as $listener) { $listener->listenEvent($formatter, $event, $eventName); } } ... }
  • 65. Event listeners Behat has 2 types of listeners: 1. Printers 2. Flow controllers
  • 66. final class StepListener implements EventListener { public function listenEvent(Formatter $formatter, Event $event, $eventName) { $this->captureScenarioOnScenarioEvent($event); $this->forgetScenarioOnAfterEvent($eventName); $this->printStepSetupOnBeforeEvent($formatter, $event); $this->printStepOnAfterEvent($formatter, $event); } ... }
  • 68. class FirstBackgroundFiresFirstListener implements EventListener { public function __construct(EventListener $descendant) { $this->descendant = $descendant; } public function listenEvent(Formatter $formatter, Event $event, $eventName) { $this->flushStatesIfBeginningOfTheFeature($eventName); $this->markFirstBackgroundPrintedAfterBackground($eventName); if ($this->isEventDelayedUntilFirstBackgroundPrinted($event)) { $this->delayedUntilBackgroundEnd[] = array($event, $eventName); return; } $this->descendant->listenEvent($formatter, $event, $eventName); $this->fireDelayedEventsOnAfterBackground($formatter, $eventName); } }
  • 69. where composite and CoR are useful?
  • 70.
  • 71. interface StepTester { public function setUp(Environment $env, FeatureNode $feature, StepNode $step, $skip); public function test(Environment $env, FeatureNode $feature, StepNode $step, $skip); public function tearDown(Environment $env, FeatureNode $feature, StepNode $step, $skip, StepResult $result); }
  • 72. problem: we need to introduce backwards incompatible change into the API
  • 74. interface ScenarioStepTester { public function setUp(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip); public function test(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip); public function tearDown(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip, StepResult $result); }
  • 75. final class StepToScenarioTesterAdapter implements ScenarioStepTester { public function __construct(StepTester $stepTester) { ... } public function setUp(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip) { return $this->stepTester->setUp($env, $feature, $step, $skip); } public function test(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip) { return $this->stepTester->test($env, $feature, $step, $skip); } public function tearDown(Environment $env, FeatureNode $feature, ScenarioNode $scenario, StepNode $step, $skip, StepResult $result) { return $this->stepTester-> tearDown($env, $feature, $step, $skip); } }
  • 76. final class StepTesterAdapterPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, ‘tester.step_wrapper’); foreach ($references as $reference) { $id = (string) $reference; $renamedId = $id . ‘.adaptee’; $adapteeDefinition = $container->getDefinition($id); $reflection = new ReflectionClass($adapteeDefinition->getClass()); if (!$reflection->implementsInterface(‘StepTester’)) { return; } $container->removeDefinition($id); $container->setDefinition( $id, new Definition(‘StepToScenarioTesterAdapter’, array( $adapteeDefinition )); ); } } }
  • 77. where adapter is useful?
  • 78. demo
  • 80. backwards compatibility Backwards compatibility in Behat comes from the extensibility. 1. Everything is extension 2. New features are extensions too 3. New features could be toggled on/off
  • 82. performance implications · 2x more objects in v3 than in v2 · Value objects are used instead of simple types · A lot of additional concepts throughout · It must be slow
  • 84.
  • 85. how?
  • 89. how?
  • 90. Step1: Close the doors Assume you have no extension points by default. 1. Private properties 2. Final classes
  • 91. Step 2: Open doors properly when you need them 1. Identify the need for extension points 2. Make extension points explicit
  • 94. class BundleFeatureLocator extends FilesystemFeatureLocator { public function locateSpecifications(Suite $suite, $locator) { if (!$suite instanceof SymfonyBundleSuite) { return new noSpecificationsIterator($suite); } $bundle = $suite->getBundle(); if (0 !== strpos($locator, '@' . $bundle->getName())) { return new NoSpecificationsIterator($suite); } $locatorSuffix = substr($locator, strlen($bundle->getName()) + 1); return parent::locateSpecifications($suite, $bundle->getPath() . '/Features' . $locatorSuffix); } }
  • 95. final class BundleFeatureLocator implements SpecificationLocator { public function __construct(SpecificationLocator $baseLocator) { ... } public function locateSpecifications(Suite $suite, $locator) { if (!$suite instanceof SymfonyBundleSuite) { return new noSpecificationsIterator($suite); } $bundle = $suite->getBundle(); if (0 !== strpos($locator, '@' . $bundle->getName())) { return new NoSpecificationsIterator($suite); } $locatorSuffix = substr($locator, strlen($bundle->getName()) + 1); return $this->baseLocator->locateSpecifications($suite, $bundle->getPath() . '/Features' . $locatorSuffix); } }
  • 96. the most closed most extensible testing framework
  • 97. ask questions close Feed! L♻♻ps: https://joind.in/11559