Welcome to Abdul Malik Ikhsan's Blog

Start Using symfony3 Beta1 Standard Edition

Posted in Symfony3 Framework, Tutorial PHP by samsonasik on November 21, 2015

Symfony 3.0.0-BETA1 released last week, while we can wait for the installer for Symfony Standard Edition to support version 3, we can use it now via composer create project. Here we go, we can run in terminal the following command:

$ composer create-project symfony/framework-standard-edition sf3-std "v3.0.0-BETA1"

When installation complete, we can see the directory structure that slightly different then v2:

sf3-std-structure

The structure for v3 is more like file system, where the executable file when old place is on app, now located in bin directory, and there is var directory to save cache, logs, and sessions.

Ok, now, let’s run server with console command:

$ php bin/console server:run

And we can see the following:

sf3-server-run

We can call 127.0.0.1:8000 in the browser, then:

sf3-localhost

Very cool! Didn’t try it? You actually need to give a try!

References:

Testing Zend Framework 2 application using phpspec

Posted in testing, Tutorial PHP, Zend Framework 2 by samsonasik on November 8, 2015

If you’re going to start working with new ZF2 application, it is be a good chance to use phpspec for testing tool. We can describe specification and generate code that we already describe. Ok, let’s start with clone ZF2 skeleton application:

$ composer create-project zendframework/skeleton-application:dev-master zfnew

We will start application with “test first”, so, we can remove current module/Application’s classes:

$ cd zfnew
$ rm -rf module/Application/Module.php
$ rm -rf module/Application/src/Application/Controller/IndexController.php

The next step is setup requiring phpspec dependency and its needed extensions:

$ composer config bin-dir bin
$ composer require phpspec/phpspec:~2.3.0 \
                   henrikbjorn/phpspec-code-coverage:~1.0.1 \
                   ciaranmcnulty/phpspec-typehintedmethods:~1.1 --dev

We will use henrikbjorn/phpspec-code-coverage for code coverage generation, and ciaranmcnulty/phpspec-typehintedmethods for typehint generation when running phpspec.

By default, our Application module follow PSR-0 autoloader, so we need to define it in composer.json:

// ...
   "autoload": {
        "psr-0": {
            "Application\\": "module/Application/src/"
        }
    },
// ...

To make it registered in composer’s autoload, we need to run dump-autoload:

$ composer dump-autoload

To point spec to describe Application namespace inside module/Application/src, we need to setup phpspec config under phpspec.yml, we can place it in root zfnew project:

# zfnew/phpspec.yml
suites:
  application_suite:
    namespace: Application
    src_path: module/Application/src/
    spec_path: module/Application

extensions:
  - PhpSpec\Extension\CodeCoverageExtension
  - Cjm\PhpSpec\Extension\TypeHintedMethodsExtension

code_coverage:
  format:
    - html
    - clover
  whitelist:
    - module/Application/src
  output:
    html: coverage
    clover: build/logs/clover.xml

Ok, let’s generate our first spec:

$ bin/phpspec desc Application/Module

We will get output like the following:

desc-1-module-class

We will get generated first spec like the following:

// module/Application/spec/Application/ModuleSpec.php
namespace spec\Application;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ModuleSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('Application\Module');
    }
}

And when we run:

$ bin/phpspec run

We will get generated class like this if we choose ‘Y’ answering “Do you want me to create Application\Module for you?” question:

run-1-module-class

And we will get a Module class inside module/Application/src/Application directory:

namespace Application;

class Module
{
}

We need to have more examples to achieve standard Module class, that has getConfig() method, and especially for Application module, we need onBootstrap(MvcEvent $e) method, so we can write examples like the following:

// module/Application/spec/Application/ModuleSpec.php
namespace spec\Application;

use Application\Module;
use PhpSpec\ObjectBehavior;
use Zend\EventManager\EventManager;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;

class ModuleSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(Module::class);
    }

    function it_return_config()
    {
        $getConfig = $this->getConfig();

        $getConfig->shouldBeArray();
        $getConfig->shouldReturn(
            include __DIR__ . '/../../config/module.config.php'
        );
    }

    function its_bootstrap(MvcEvent $e, $application, $eventManager)
    {
        $application->beADoubleOf(Application::class);
        $eventManager->beADoubleOf(EventManager::class);

        $application->getEventManager()->willReturn($eventManager)->shouldBeCalled();
        $e->getApplication()->willReturn($application)->shouldBeCalled();

        $this->onBootstrap($e);
    }
}

And when run, we will get the following errors:

                                                                                
  Do you want me to create `Application\Module::getConfig()` for you? [Y/n] 
  Y
  Method Application\Module::getConfig() has been created.
                                                                                
  Do you want me to create `Application\Module::onBootstrap()` for you? [Y/n] 
  Y
  
  Method Application\Module::onBootstrap() has been created.
Application/Module                                                                
  18  - it return config
      is_array(null) expected to return true, but it did not.

Application/Module                                                                
  28  - its bootstrap
      some predictions failed:
        Double\Zend\Mvc\MvcEvent\P2:
          No calls have been made that match:
            Double\Zend\Mvc\MvcEvent\P2->getApplication()
          but expected at least one.  Double\Zend\Mvc\Application\P1:
          No calls have been made that match:
            Double\Zend\Mvc\Application\P1->getEventManager()
          but expected at least one.

            33%                                     66%                          3
1 specs
3 examples (1 passed, 2 failed)
708ms

Don’t worry about it, it is normal, we just need to fulfill what already described in code as we have generated code template:

namespace Application;

class Module
{

    public function getConfig()
    {
        // TODO: write logic here
    }

    public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent)
    {
        // TODO: write logic here
    }
}

let’s fill it so it looks like:

namespace Application;

use Zend\Mvc\ModuleRouteListener;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/../../config/module.config.php';
    }

    public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent)
    {
        $eventManager        = $mvcEvent->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);
    }
}

To prove, you can re-run bin/phpspec run and everything will be green ;).

Now, let’s create spec for Application\Controller\IndexController:

$ bin/phpspec desc Application/Controller/IndexController

And we can define the IndexControllerSpec:

// module/Application/spec/Application/Controller/IndexControllerSpec.php
namespace spec\Application\Controller;

use Application\Controller\IndexController;
use PhpSpec\ObjectBehavior;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class IndexControllerSpec extends ObjectBehavior
{
    function let(ViewModel $viewModel)
    {
        $this->beConstructedWith($viewModel);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType(IndexController::class);
    }

    function it_is_extends_abstract_action_controller()
    {
        $this->shouldBeAnInstanceOf(AbstractActionController::class);
    }

    function its_index_action_return_view_model(ViewModel $viewModel)
    {
        $this->indexAction()->shouldReturn($viewModel);
    }
}

We use beConstructedWith(), so, we need to inject ViewModel into controller’s construction. We can run bin/phpspec run and we will get the following code:

// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;

class IndexController
{

    public function __construct(\Zend\View\Model\ViewModel $viewModel)
    {
        // TODO: write logic here
    }

    public function indexAction()
    {
        // TODO: write logic here
    }
}

Let’s fulfill the examples as described in spec:

// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    private $viewModel;

    public function __construct(\Zend\View\Model\ViewModel $viewModel)
    {
        $this->viewModel = $viewModel;
    }

    public function indexAction()
    {
        return $this->viewModel;
    }
}

We need to build the Controller with Factory, so we can describe the factory:

$ bin/phpspec desc Application/Factory/Controller/IndexControllerFactory

We can write spec examples:

// module/Application/spec/Application/Factory/Controller/IndexControllerFactorySpec.php
namespace spec\Application\Factory\Controller;

use Application\Controller\IndexController;
use Application\Factory\Controller\IndexControllerFactory;
use PhpSpec\ObjectBehavior;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class IndexControllerFactorySpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(IndexControllerFactory::class);
    }

    function it_is_implements_factory_interface()
    {
        $this->shouldImplement(FactoryInterface::class);
    }

    function it_is_create_indexcontroller(ServiceLocatorInterface $serviceLocator)
    {
        $this->createService($serviceLocator)
             ->shouldReturnAnInstanceOf(IndexController::class);
    }
}

When run bin/phpspec run, we will get generated code:

// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php
namespace Application\Factory\Controller;

class IndexControllerFactory
{

    public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface)
    {
        // TODO: write logic here
    }
}

Let’s modify to fulfill the spec examples:

// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php
namespace Application\Factory\Controller;

use Application\Controller\IndexController;
use Zend\ServiceManager\FactoryInterface;
use Zend\View\Model\ViewModel;

class IndexControllerFactory implements FactoryInterface
{
    public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface)
    {
        $viewModel = new ViewModel();
        return new IndexController($viewModel);
    }
}

So, everything looks good, we can run bin/phpspec run again, and we can get green result again:

run-2-phpspec

We can see the coverage result in coverage/index.html:

cov-phpspec-zf2

Now, to make our ZF2 application still works when call ‘/’ in browser, we can update our module/Application/config/module.config.php:

// ...
    'controllers' => array(
        'factories' => array(
            'Application\Controller\Index' => 'Application\Factory\Controller\IndexControllerFactory'
        ),
    ),
// ...

That’s it 😉