Post on 10-May-2015
PhpSpec extension pointsfor version 2.0.0
About me PHP Software engineer KrakDevs meetups organizer Huge fan of BDD/TDD Part of Coduo
Web:https://github.com/norzechowicz https://twitter.com/norzechowicz http://coduo.plhttp://krakdevs.plNorbert Orzechowicz
This is everything you need. Trust me ;)
Ok, so how PhpSpec works?
Just like a regular Symfony2 console
application
Read more http://symfony.com/doc/current/components/console/index.html
Ok, but what happens when user execute „run”
command?
$ bin/phpspec run
#!/usr/bin/env php!<?php!//bin/phpspec!!$app = new PhpSpec\Console\Application(PHPSPEC_VERSION);!
Initialize console application<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!/**! * The command line application entry point! */!class Application extends BaseApplication!{! public function __construct($version)! {! }!}!
Create service container<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!/**! * The command line application entry point! */!class Application extends BaseApplication!{! public function __construct($version)! {! $this->container = new ServiceContainer;! parent::__construct('phpspec', $version);! }!}!
In the name of dependency injection!
Service Container
<?php!///phpspec/src/PhpSpec/ServiceContainer.php!namespace PhpSpec;!!class ServiceContainer!{! // Sets a object or a callback for the object creation. ! // A new object will be created every time! public function set($id, $value);!! // Sets a object or a callback for the object creation. ! // The same object will be returned every time! public function setShared($id, $callable);!! // Retrieves a service from the container! public function get($id);!! // Retrieves a list of services of a given prefix! public function getByPrefix($prefix);!}!
Explanation of the most important methods
Register service example<?php!///phpspec/src/Acme/MyClass.php!namespace Acme;!!class MyClass!{! public function setup(ServiceContainer $container)! {! $container->set('acme.service.foo', function ($c) {! return new Foo();! });!! $container->setShared('acme.service.bar', function ($c) {! return new Bar();! });!! $container->getByPrefix('acme.service');! // will return acme.service.foo as Foo incance & ! ! ! // acme.service.bar as Bar instance! }!}!
#!/usr/bin/env php!<?php!//bin/phpspec!!$app = new PhpSpec\Console\Application(PHPSPEC_VERSION);!$app->run();!
Run! <?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!/**! * The command line application entry point! */!class Application extends BaseApplication!{! public function doRun(InputInterface $input, OutputInterface $output)! {! $this->setupContainer($this->container); ! }!}!
Not so fast, setup service container first
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! $this->setupLoader($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! $this->setupLoader($container);! $this->setupFormatter($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! $this->setupLoader($container);! $this->setupFormatter($container);! $this->setupRunner($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! $this->setupLoader($container);! $this->setupFormatter($container);! $this->setupRunner($container);! $this->setupCommands($container);! }!}!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! $this->setupIO($container);! $this->setupEventDispatcher($container);! $this->setupGenerators($container);! $this->setupPresenter($container);! $this->setupLocator($container);! $this->setupLoader($container);! $this->setupFormatter($container);! $this->setupRunner($container);! $this->setupCommands($container);!! $this->loadConfigurationFile($container);! }!}!
RUN!
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! public function doRun(InputInterface $input, OutputInterface $output)! {! $this->setupContainer($this->container);! ! foreach ($this->container->getByPrefix('console.commands') as $command) {! $this->add($command);! }! ! return parent::doRun($input, $output);! }!}!
But first add commands. By default „run” and „describe”.
Application is ready what now?
Run command
<?php!//phpspec/src/PhpSpec/Console/Command/RunCommand.php!namespace PhpSpec\Console\Command;!!class RunCommand extends Command!{! protected function configure()! {! // configure input options, arguments, command name! }!! protected function execute(InputInterface $input, OutputInterface $output)! {! // run suite with runner.suite service!! }!}!
There is no magic here, just simple Symfony2 console command.
Suite you say?
Space where specifications exists
Suite
Specifications?
Spaces where examples exists
Suite
FooClassSpec.php - Specification
BarClassSpec.php - Specification
FazClassSpec.php - Specification
BazClassSpec.php - Specification
Examples!?
Yes, functions that describe object behavior (tests)
Suite
FooClassSpec.php - Specification
function it_is_awesome() - Example
function it_do_awesome_stuff() - Example
function it_is_famous() - Example
Make sense now!
Run command simplified algorithm
1. Localize suite - Locator 2. Load suite - Loader 3. Run suite - Suite Runner 4. Run specifications - Specification Runner5. Run examples - Example Runner
And what about extensions?
Remember container setup?
<?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupContainer(ServiceContainer $container)! {! // . . . ! $this->loadConfigurationFile($container);! }!}!
phpspec.yml suites: acme_suite: namespace: Acme\TheLib spec_prefix: acme_spec ! # shortcut for # my_suite: # namespace: The\Namespace my_suite: The\Namespace !extensions:! - PhpSpec\Symfony2Extension\Extension
Extension interface<?php!//phpspec/src/PhpSpec/Extension/ExtensionInterface.php!namespace PhpSpec\Extension;!!use PhpSpec\ServiceContainer;!!interface ExtensionInterface!{! /**! * @param ServiceContainer $container! */! public function load(ServiceContainer $container);!}!
That’s all?
Simply beautiful, right?
But what can I do with it?
But what can I do with it? Register event listeners as a services Replace existing services Register custom example maintainers Register new fomatters Register new code generators Replace suite locator Register new matchers Register new commands !And many many more…
How?
Let me show you how to register an event listener.
Event subscriber class<?php!///src/Coduo/PhpSpec/Listener/DataProviderListener.php!namespace Coduo\PhpSpec\Listener;!!class DataProviderListener implements EventSubscriberInterface!{! public static function getSubscribedEvents()! {! return array(! 'beforeSpecification' => array('beforeSpecification'),! );! }!! public function beforeSpecification(SpecificationEvent $event)! {! // listener logic here ! }!}!
PhpSpec extension class<?php!//src/Coduo/PhpSpec/DataProviderExtension.php!namespace Coduo\PhpSpec;!!class DataProviderExtension implements ExtensionInterface!{! public function load(ServiceContainer $container)! {! $container->set(! 'event_dispatcher.listeners.data_provider', ! function ($c) {! return new DataProviderListener();! }! );! }!}!
And how PhpSpec know that my service is an
event listener?
Just use proper prefix <?php!//phpspec/src/PhpSpec/Console/Application.php!!use Symfony\Component\Console\Application as BaseApplication;!!class Application extends BaseApplication!{! protected function setupEventDispatcher(ServiceContainer $container)! {! $container->setShared('event_dispatcher', function ($c) {! $dispatcher = new EventDispatcher;!! array_map(! array($dispatcher, 'addSubscriber'),! $c->getByPrefix('event_dispatcher.listeners')! );!! return $dispatcher;! });! }!}!
Are there any other prefixes that I can use?
List of prefixes that you can use by default.
event_dispatcher.listeners - EventSubscriberInterfac code_generator.generators - GeneratorInterfac formatter.presenter.differ.engines - Differ\EngineInterface locator.locators - ResourceLocatorInterface runner.maintaners - MaintainerInterface console.commands - Command
Thank you for your attention
Questions?