Welcome to Abdul Malik Ikhsan's Blog

Start Using Middleware Approach with new zend-mvc

Posted in Tutorial PHP, Zend Framework, Zend Framework 2 by samsonasik on March 1, 2016

zend-mvc 2.7.0 is coming, beside of the forward compatibility with V3 components, there is new middleware listener that allow us to do Dispatching PSR-7 middleware. The middleware can be an invokable class with __invoke() method like the following:

function __invoke($request, $response)
{
    $response->getBody()->write('Hello World!');

    return $response;
}

Ok, let’s start try it, create new project:

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

After composer create-project done, as usual, you will get new project. zend-mvc 2.7.0 released today, so, You should get zend-mvc 2.7.0 already by run:

$ composer update

Now, We can create a middleware, for example, a HomeAction middleware:

namespace Application\Middleware;

class HomeAction
{
    public function __invoke($request, $response)
    {
        $response->getBody()->write('Hello World!');
        return $response;
    }
}
// module/Application/src/Application/Middleware/HomeAction.php

We then can replace the ‘home’ route:

namespace Application;

// ...
    'home' => [
        'type' => 'Literal',
        'options' => [
            'route' => '/',
            'defaults' => [
                'middleware' => Middleware\HomeAction::class,
            ],
        ],
    ],
// ...
// module/Application/config/module.config.php

As the Application\Middleware\HomeAction is a service, then it need to be registered in service_manager:

namespace Application;

use Zend\ServiceManager\Factory\InvokableFactory;

// ...
    'service_manager' => [
        'factories' => [
            Middleware\HomeAction::class => InvokableFactory::class,
        ],
    ]
// ...
// module/Application/config/module.config.php

Everything seems configured correctly, now, let’s start the server:

$ php -S localhost:8080 -t public

And open up in the browser: http://localhost:8080 ! So, the “Hello World!” will be shown!

Work with ViewModel

So, you now want to work with ViewModel with its layout, You can! Let’s do it. You can inject the Middleware with the Renderer and ViewManager.

use Zend\View\Renderer\PhpRenderer;
use Zend\Mvc\View\Http\ViewManager;

class HomeAction
{
    // ...
    public function __construct(
        PhpRenderer $renderer,
        ViewManager $view
    ) {
        $this->renderer = $renderer;
        $this->view = $view;
    }
    // ...
}
// module/Application/src/Application/Middleware/HomeAction.php

To make it work, we can create factory for it:

namespace Application\Middleware;

class HomeActionFactory
{
    public function __invoke($container)
    {
        $viewRenderer = $container->get('ViewRenderer');
        $viewManager  = $container->get('ViewManager');

        return new HomeAction($viewRenderer, $viewManager);
    }
}
// module/Application/src/Application/Middleware/HomeActionFactory.php

Based on the factory above, we then need to update the registered Application\Middleware\HomeAction service:

namespace Application;

// ...
    'service_manager' => [
        'factories' => [
            Middleware\HomeAction::class => Middleware\HomeActionFactory::class,
        ],
    ]
// ...
// module/Application/config/module.config.php

So, now, you can update the Middleware as follows:

use Zend\View\Renderer\PhpRenderer;
use Zend\Mvc\View\Http\ViewManager;
use Zend\View\Model\ViewModel;
use Zend\Diactoros\Response\HtmlResponse;

class HomeAction
{
    // ...
    public function __invoke($request, $response)
    {
        $viewModel = new ViewModel();
        $viewModel->setTemplate('application/index/index');
        
        $layout = $this->view->getViewModel();
        $layout->setVariable(
            'content',
            $this->renderer->render($viewModel)
        );

        return new HtmlResponse($this->renderer->render($layout));
    }
    // ...
}
// module/Application/src/Application/Middleware/HomeAction.php

Done!😉

References:
https://gist.github.com/weierophinney/b9dbff92e4446f49e248
https://github.com/weierophinney/2015-10-22-ZF3

Testing Hard Dependency with AspectMock

Posted in Teknologi, Tutorial PHP by samsonasik on January 28, 2016

This is another testing legacy application post. Don’t tell your client/other people to refactor, if your job is only to make tests for it, as at some situations, there are reasons to not refactor. You may have situation to test hard dependency that impossible to be mocked and stubbed. There is a library named AspectMock for it, that you can use in PHPUnit, for example.

So, to have it, you can require via composer:

composer require "codeception/aspect-mock:^0.5.5" --dev

For example, you have the following class:

namespace App;

class MyController
{
    public function save()
    {
        $user = new User();
        if (! $user->save()) {
            echo 'not saved';
            return;
        }

        echo 'saved';
    }
}

That bad, huh! Ok, let’s deal to tests it even you don’t really like it. First, setup your phpunit.xml to have ‘backupGlobals=”false”‘ config:

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
    colors="true"
    backupGlobals="false"
    bootstrap="bootstrap.php">
    <testsuites>
        <testsuite name="AppTest">
            <directory suffix=".php">./test</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist addUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./src</directory>
        </whitelist>
    </filter>
</phpunit>

Now, based on config above, you can create bootstrap.php:

include 'vendor/autoload.php';

use AspectMock\Kernel;

$kernel = Kernel::getInstance();
$kernel->init([
    'debug' => true,
    'cacheDir' => __DIR__ . '/data/cache',
    'includePaths' => [__DIR__.'/src'],
]);

Assumption: You have ‘./data/cache’ for saving cache and ‘src/’ for your source code directory, if you use your own autoloader, you can add:

// ...
$kernel->loadFile('YourAutoloader.php');

as the AspectMock documentation mentioned.

Now, time to write the tests:

  1. Preparation
    namespace AppTest;
    
    use PHPUnit_Framework_TestCase;
    use App\MyController;
    use AspectMock\Test as test;
    
    class MyControllerTest extends PHPUnit_Framework_TestCase
    {
        private $myController;
    
        protected function setUp()
        {
            $this->myController = new MyController;
        }
    
        protected function tearDown()
        {
            test::clean(); // remove all registered test doubles
        }
    }
    

  2. write the test cases

class MyControllerTest extends PHPUnit_Framework_TestCase
{
    // ...
    public function provideSave()
    {
        return [
            [true, 'saved'],
            [false, 'not saved'],
        ];
    }

    /**
     * @dataProvider provideSave
     */
    public function testSave($succeed, $echoed)
    {
        // mock
        $userMock = test::double('App\User', ['save' => $succeed]);

        ob_start();
        $this->myController->save();
        $content = ob_get_clean();
        $this->assertEquals($echoed, $content);

        // stub
        $userMock->verifyInvoked('save');
    }
    // ...
}

Done😉

references:
https://github.com/Codeception/AspectMock
https://twitter.com/grmpyprogrammer/status/642847787713884160
https://littlehart.net/atthekeyboard/2014/12/14/stop-telling-me-to-refactor/

Monkey Patch PHP Quit Statement with Patchwork

Posted in Tutorial PHP by samsonasik on January 18, 2016

If your job is make tests for legacy app that has exit() or die(); everywhere, and you don’t have privilege to refactor them, make tests for them may be hard as the test aborted when the quit statements executed in the tests. To test them, we need to redefine the user-defined functions and methods at runtime, and there is a lib for that, it is named Patchwork.

We can run command:

$ composer require antecedent/patchwork:*

to get the Patchwork dependency.

Let’s see how it can work, let say, we have a class:

namespace App;

class MyClass
{
    public function foo($arg)
    {
        if ($arg === 1) {
            return true;
        }

        exit('app exit.');
    }
}

Seeing the MyClass::foo, we can only tests if the $arg is equal then 1, otherwise, we need to redefine it, and there is a Patchwork\replace() method for it by call like the following:

replace(MyClass::class. '::foo', function($arg) {
    if ($arg === 1) {
        pass();
    }
    return 'app exit.';
});

The pass() method will call original method functionality if $arg === 1 as that not return quit statement, otherwise we redefine to return string with value = ‘app exit’.

We can define in our unit test like this MyClassTest class:

namespace AppTest;

use PHPUnit_Framework_TestCase;
use App\MyClass;
use function Patchwork\pass;
use function Patchwork\replace;
use function Patchwork\undoAll;

class MyClassTest extends PHPUnit_Framework_TestCase
{
    private $myclass;

    protected function setUp()
    {
        replace(MyClass::class. '::foo', function($arg) {
            if ($arg === 1) {
                pass();
            }
            return 'app exit.';
        });

        $this->myclass = new MyClass;
    }

    protected function tearDown()
    {
        undoAll();
    }
}

We can call the replace it in setUp(), and to undo, we can call undoAll() in tearDown(). And now, we can add the tests into MyClassTest tests :

// ...

    public function provideFoo()
    {
        return [
            [1, true],
            [0, 'app exit.'],
        ];
    }

    /**
     * @dataProvider provideFoo
     */
    public function testFoo($arg, $result)
    {
        $this->assertSame($result, $this->myclass->foo($arg));
    }

// ...

References to read:
http://kahlan.readthedocs.org/en/latest/monkey-patching/
http://antecedent.github.io/patchwork/
http://afilina.com/testing-methods-that-make-static-calls/

Using Routed Middleware class as Controller with multi actions in Expressive

Posted in Tutorial PHP, Zend Framework by samsonasik on January 3, 2016

Note: this post is now part of Zend\Expressive cookbook.

multi-action-1-middleware
If you are familiar with frameworks with provide controller with multi actions functionality, like in Zend Framework 1 and 2, you may want to apply it when you use Zend\Expressive microframework as well. Usually, we need to define 1 routed middleware, 1 __invoke() with 3 parameters ( request, response, next ). If we need another specifics usage, we can create another routed middleware classes, for example:

  1. AlbumPageIndex
  2. AlbumPageEdit
  3. AlbumPageAdd

What if we want to use only one middleware class which facilitate 3 pages above? We can with make request attribute with ‘action’ key via route config, and validate it in __invoke() method with ReflectionMethod.

Let say, we have the following route config:

// ...
    'routes' => [
        [
            'name' => 'album',
            'path' => '/album[/:action][/:id]',
            'middleware' => Album\Action\AlbumPage::class,
            'allowed_methods' => ['GET'],
        ],
    ],
// ...

To avoid repetitive code for modifying __invoke() method, we can create an AbstractPage, like the following:

namespace App\Action;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionMethod;

abstract class AbstractPage
{
    public function __invoke($request, $response, callable $next = null)
    {
        $action = $request->getAttribute('action', 'index') . 'Action';

        if (method_exists($this, $action)) {
            $r = new ReflectionMethod($this, $action);
            $args = $r->getParameters();

            if (count($args) === 3
                && $args[0]->getType() == ServerRequestInterface::class
                && $args[1]->getType() == ResponseInterface::class
                && $args[2]->isCallable()
                && $args[2]->allowsNull()
            ) {
                return $this->$action($request, $response, $next);
            }
        }

        return $next($request, $response->withStatus(404), 'Page Not Found');
    }
}

In above abstract class with modified __invoke() method, we check if the action attribute, which default is ‘index’ if not provided, have ‘Action’ suffix, and the the method is exists within the middleware class with 3 parameters with parameters with parameter 1 as ServerRequestInterface, parameter 2 as ResponseInterface, and parameter 3 is a callable and allows null, otherwise, it will response 404 page.

So, what we need to do in out routed middleware class is extends the AbstractPage we created:

namespace Album\Action;

use App\Action\AbstractPage;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Expressive\Template;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class AlbumPage extends AbstractPage
{
    protected $template;    
    // you need to inject via factory 
    public function __construct(Template\TemplateRendererInterface $template)
    { $this->template = $template; }

    public function indexAction(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        return new HtmlResponse($this->template->render('album::album-page'));
    }

    public function addAction(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        return new HtmlResponse($this->template->render('album::album-page-add'));
    }

    public function editAction(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ) {
        $id = $request->getAttribute('id');
        if ($id === null) {
            throw new \InvalidArgumentException('id parameter must be provided');
        }

        return new HtmlResponse(
            $this->template->render('album::album-page-edit', ['id' => $id])
        );
    }
}

The rest is just create the view. Done😉

Tagged with:

Compose modular Expressive application with “ZF2 style” structure

Posted in Tutorial PHP, Zend Framework by samsonasik on December 28, 2015

expressive-modular-structure-strategy Yes, there is a doc for compose modular application in Zend\Expressive. And No, I won’t debate about how non-shareable module shouldn’t be in other modules (only “root” module), which is on personal preference. In this post, I just want to show you how to compose modular application like the “ZF2” structure, with config, view, and class that require the config per-“module”, and that’s do-able!.

Let’s do it!:

1) Same as doc, requiring mtymek/expressive-config-manager:

$ composer require mtymek/expressive-config-manager

2) Register your modules as namespace in composer autoload:

// composer.json
// ...
    "autoload": {
        "psr-4": {
            "App\\": "src/App/",
            "Album\\": "src/Album/"
        }
    },
// ...

3) Run dump-autoload

$ composer dump-autoload

4) Let’s extract one by one per-module, “App” and “Album”
a. src/App/AppConfig.php

//src/App/AppConfig.php
namespace App;

class AppConfig
{
    public function __invoke()
    {
        return include __DIR__ . '/config/expressive-config.php';
    }
}

b. src/Album/AlbumConfig.php

//src/Album/AlbumConfig.php
namespace Album;

class AlbumConfig
{
    public function __invoke()
    {
        return include __DIR__ . '/config/expressive-config.php';
    }
}

c. src/App/config/expressive-config.php
You can move various arrays from config/autoload/routes.global.php to src/App/config/expressive-config.php:

return [
    
    'dependencies' => [
        'invokables' => [
            App\Action\PingAction::class => App\Action\PingAction::class,
        ],
        'factories' => [
            App\Action\HomePageAction::class => App\Action\HomePageFactory::class,
        ],
    ],
    
    'routes' => [
        [
            'name' => 'home',
            'path' => '/',
            'middleware' => App\Action\HomePageAction::class,
            'allowed_methods' => ['GET'],
        ],
        [
            'name' => 'api.ping',
            'path' => '/api/ping',
            'middleware' => App\Action\PingAction::class,
            'allowed_methods' => ['GET'],
        ],
    ],
];

d. src/Album/config/expressive-config.php

Like App config, you can define:

return [
    
    'dependencies' => [
        'factories' => [
            Album\Action\AlbumPageAction::class => Album\Action\AlbumPageFactory::class,
        ],
    ],
    
    'routes' => [
        [
            'name' => 'album',
            'path' => '/album',
            'middleware' => Album\Action\AlbumPageAction::class,
            'allowed_methods' => ['GET'],
        ],
    ],
];

5) Templates
As in the “App” and “Album” modules, at this case, we need template, we can define templates path in the config:

a. for App config

// src/App/config/expressive-config.php
return [
    // ...
    'templates' => [
        'paths' => [
            'app'    => [ __DIR__ . '/../templates/app'],
        ],
    ], 
];

Note:
As it will redundant with “app” key registered in config/autoload/templates.global.php, you need to remove “app” key under “paths” in config/autoload/templates.global.php. Don’t forget to remove the “app” directory inside rootproject/templates to avoid confusion.

b. for Album config

// src/Album/config/expressive-config.php
return [
    // ...
    'templates' => [
        'paths' => [
            'album'    => [ __DIR__ . '/../templates/album'],
        ],
    ],
];

Now, you can have the following structure:
expressive-modular-structure-strategy-template

6) Register the “modules” in config/config.php:

use Zend\Expressive\ConfigManager\ConfigManager;
use Zend\Expressive\ConfigManager\PhpFileProvider;

$configManager = new ConfigManager([
    App\AppConfig::class,
    Album\AlbumConfig::class,
    // ...
    
    new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
]);

return new ArrayObject($configManager->getMergedConfig());

It’s done! You can continue with writing middlewares! Tired to follow the steps? Just clone and install from here: https://github.com/samsonasik/learn-expressive-modular

Using Expressive’s middleware in Penny Framework

Posted in Penny Framework, Tutorial PHP, Zend Framework by samsonasik on December 21, 2015

If we are using Penny Framework, we can use Expressive‘s middleware. We can do that with passing callable $next that handle request and response with Penny Event. The $next is simply like the following:

$next = function($request, $response) use ($event) {
   $event->setRequest($request);
   $event->setResponse($response);
};

and we are ready to go! Let’s try to do real example. We need to use los/basepath that can apply “base path” in our application that point to classic-app/public and we can to call: /classic-app/public after our host.

$ composer require los/basepath

We need to invoke the middleware to make it called. So, if we do in ‘bootstrap’ event, we can do:

$eventManager->attach('bootstrap', function($event) {
    $next = function($request, $response) use ($event) {
        $event->setRequest($request);
        $event->setResponse($response);
    };

    $basepathMiddleware = new \LosMiddleware\BasePath\BasePath(
        '/classic-app/public'
    );
    $basepathMiddleware(
        $event->getRequest(),
        $event->getResponse(), 
        $next
    );
});

This is the case if we are using penny skeleton app and we install under ‘classic-app’ directory, and the “front controller” is in public/index.php.

For routed middleware, we can just do the register the middleware as controller:

// config/di.php
// ...
    'router' => function () {
        return \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
            $r->addRoute(
                'GET',
                '/',
                ['App\Controller\IndexController', 'index']
            );

            $r->addRoute(
                'GET',
                '/api/ping',
                ['App\Controller\PingAction', '__invoke']
            );
        });
    },
// ...

That’s it😉

Middleware implementation in Penny Framework

Posted in Penny Framework, Tutorial PHP by samsonasik on December 6, 2015

Penny is a microframework that has different idea for accomplishing middleware implementation. If we found other frameworks that implements this signatures:

function ($request, $response, $next = null);

In Penny Framework, Middleware implemented via listener that has priority that we call before or/and after. If we need to initialize application flow, you may use ‘bootstrap’ event:

// config/di.php
use App\Middleware\BootstrapMiddleware;
use function DI\decorate;

return [
    'event_manager' => decorate(function($eventManager, $container) {

        $eventManager->attach(
            'bootstrap',
            [$container->get(BootstrapMiddleware::class), 'bootstrap']
        );
        // other attach here...

        return $eventManager;
    }),

    // other service definitions here...
];

We want the middleware executed in all application flow in all controller in the end? use ‘*’ and negative priority:

// config/di.php
use App\Middleware\AllEnderMiddleware;
use function DI\decorate;

return [
    'event_manager' => decorate(function($eventManager, $container) {

        $eventManager->attach(
            '*',
            [$container->get(AllEnderMiddleware::class), 'end'],
            -1
        );
        // other attach here...

        return $eventManager;
    }),

    // other service definitions here...
];

Wanna handle for specific controller’s action ? Use following usage:

// config/di.php
use App\Controller\IndexController;
use App\Middleware\IndexMiddleware;
use function DI\decorate;

return [

    'event_manager' => decorate(function($eventManager, $container) {

            // before ...
            $eventManager->attach(
                IndexController::class.'.index',
                [$container->get(IndexMiddleware::class), 'pre'],
                1
            );

            // after ...
            $eventManager->attach(
                IndexController::class.'.index',
                [$container->get(IndexMiddleware::class), 'post'],
                -1
            );
           // other attach here...

           return $eventManager;
    }),

    // other service definitions here...
];

If we use default Penny EventManager implementation, which is usage Zend Framework 2 EventManager, higher priority will be served first, lower priority will be served later.

If you are familiar with CakePHP, there is already a CakePHP Event implementation, that will serve lower priority first, higher later.

For callable listener, we may create listener service like the following:

namespace App\Middleware;

class IndexMiddleware
{
    public function pre($event)
    {
        $request = $event->getRequest();
        $response = $event->getResponse();

        // manipulate request or/and response here
        $request = $request->withAttribute('foo', 'fooValue');

        // apply manipulated request or/and response
        $event->setRequest($request);
        $event->setResponse($response);
    }

    public function post($event)
    {
        $request = $event->getRequest();
        $response = $event->getResponse();

        // manipulate request or/and response here
        $response = $response->withHeader('X-Powered-By', 'JAVA');

        // apply manipulated request or/and response
        $event->setRequest($request);
        $event->setResponse($response);
    }
}

We can then register the middleware in our Service Container:

//config/di.php
use App\Middleware\AllEnderMiddleware;
use App\Middleware\BootstrapMiddleware;
use App\Middleware\IndexMiddleware;
use function DI\object;

return [
    // ...
    AllEnderMiddleware::class => object(AllEnderMiddleware::class),
    BootstrapMiddleware::class => object(BootstrapMiddleware::class),
    IndexMiddleware::class => object(IndexMiddleware::class),
];

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😉

PHPUnit: Testing Closure passed to Collaborator

Posted in Teknologi, Tutorial PHP by samsonasik on October 4, 2015

Yes! Closure is callable, so you can just call __invoke() to the closure returned when test it! This is happen when we, for example, have a class and function that have closure inside it like the following:

class Awesome
{
    public function call($foo)
    {
        return function() use ($foo) {
            return $foo;
        };
    }
}

This can be tested with:

use Awesome;
use PHPUnit_Framework_TestCase;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesome = new Awesome();
    }

    public function testCall()
    {
        $foo = 'foo';
        $call = $this->awesome->call($foo);
        $this->assertTrue(is_callable($call));

        $invoked = $call->__invoke();
        $this->assertEquals($foo, $invoked);
    }
}

We need an __invoke() call, as the closure never executed before it invoked. So, we need to call that.

On Collaborator Case

The problem is when we have a collaborator, and closure is processed inside the collaborator:

class Awesome
{
    private $awesomeDependency;

    public function __construct(AwesomeDependency $awesomeDependency)
    {
        $this->awesomeDependency = $awesomeDependency;
    }

    public function call($foo)
    {
        $closure = function() use ($foo) {
            return $foo;
        };
        return $this->awesomeDependency->call($closure);
    }
}

and the closure inside call only executed in the AwesomeDependency class:

class AwesomeDependency
{
    public function call($call)
    {
        return $call();
    }
}

Our test can be like the following:

use Awesome;
use AwesomeDependency;
use PHPUnit_Framework_TestCase;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesomeDependency = $this->prophesize(AwesomeDependency::class);
        $this->awesome     = new Awesome($this->awesomeDependency->reveal());
    }

    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };

        $this->awesomeDependency
             ->call($closure)
             ->will(function() use ($closure) {
                return $closure->__invoke();
             })
             ->shouldBeCalled();

        $call = $this->awesome->call($foo);
    }
}

As we can see, the $this->awesomeDependency is act as a mock, and calling __invoke() in will() is represent a $closure that already passed to the mock, not the original $closure one, and we will get partial coverage:

closure-call-collaborator1

We know now, it won’t coverable as completed! What we can do? A refactor! But wait, it may be a legacy code, an aggressive refactor may cause problem, so a little but works patch may work for it.

  • Make a $closure as class property, and add mutator and accessor for it.
class Awesome
{
    private $closure;

    // ...

    public function setClosure($closure)
    {
        $this->closure = $closure;
    }

    public function getClosure()
    {
        return $this->closure;
    }

    // ...
}
  • Set $closure property when call call() function:
class Awesome
{
    // ...
    public function call($foo)
    {
        $this->setClosure(function() use ($foo) {
            return $foo;
        });

        return $this->awesomeDependency->call($this->getClosure());
    }
}
  • And in tests, we can now has:
class AwesomeTest extends PHPUnit_Framework_TestCase
{
    // ...
    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };
        $awesome = $this->awesome;

        $this->awesomeDependency
             ->call($closure)
             ->will(function() use ($awesome) {
                return $awesome->getClosure()->__invoke();
            })
            ->shouldBeCalled();

        $call = $this->awesome->call($foo);
        $this->assertEquals($foo, $call);
    }
}

closure-call-collaborator2

Need a better way? We can replace a closure with an array callback, so, we add additional function that called via call_user_func_array():

class Awesome
{
    public function call($foo)
    {
        return $this->awesomeDependency->call(call_user_func_array(
            [$this, 'onFoo'],
            [$foo]
        ));
    }

    public function onFoo($foo)
    {
        return function() use ($foo) {
            return $foo;
        };
    }
}

And in our tests, we can do:

    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };
        $awesome = $this->awesome;
        $this->awesomeDependency->call($closure)
             ->will(function() use ($awesome, $foo) {
                 return $awesome->onFoo($foo)->__invoke();
             })
            ->shouldBeCalled();

        $call = $this->awesome->call($foo);
        $this->assertEquals($foo, $call);
    }

And, we now have a fully coverage too:

closure-call-collaborator3

Tagged with: ,

Testing “expects($this->any())” with Prophecy with Spying

Posted in Tutorial PHP by samsonasik on September 27, 2015

Testing method call from Collaborator that may be 0 or any other count with phpunit Framework test case can be done with expects($this->any()) from mock object. If we are using Prophecy for mocking tools, it can be done with spying.
However, the spying itself need checks whenever method is called or not. We need findProphecyMethodCalls() call againsts ObjectProphecy for that.

For example, we have a class with collaborator like the following:

namespace App;

class Awesome
{
    private $awesomeDependency;

    public function __construct(AwesomeDependency $awesomeDependency)
    {
        $this->awesomeDependency = $awesomeDependency;
    }

    public function process($data)
    {
        $rand = rand(1, 2);
        if ($rand === 1) {
            return $this->awesomeDependency->process($data);
        }
        return $data;
    }
}

The rand() usage is just a sample, in real app, we may have a heavy logic and it may fall to not call the collaborator.

The tests can be done like this:

namespace AppTest;

use App\Awesome;
use App\AwesomeDependency;
use PHPUnit_Framework_TestCase;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesomeDependency = $this->prophesize(AwesomeDependency::class);
        $this->awesome     = new Awesome($this->awesomeDependency->reveal());
    }

    public function testProcess()
    {
        $data = [
            'foo' => 'abcdef',
        ];

        // make \Prophecy\MethodProphecy instance
        $methodProphecy = $this->awesomeDependency
                               ->process(Argument::exact($data));
        
        // call method from actual instance
        $this->awesome->process($data);

        $calls = $this->awesomeDependency->findProphecyMethodCalls(
            'process',
            new ArgumentsWildcard([$data])
        );
        $count = count($calls);
        
        // assert how many times it called 
        $methodProphecy->shouldBeCalledTimes($count);
        if ($count) {
            // echoing just to prove
            echo 'Method from collaborator has been called';
            $methodProphecy->shouldHaveBeenCalled();
        } else {
            // echoing just to prove
            echo 'Method from collaborator has not been called';
            $methodProphecy->shouldNotHaveBeenCalled();
        }
    }
}

Of course, it may be can’t be called ‘expecting’ before for something has done, it may be can be called as ‘recording’ what already happen, but by this usage, we can prove if it actually called in actual code.

ZendExpressive: On Callable $next, and middleware_pipeline

Posted in Tutorial PHP, Zend Framework by samsonasik on September 20, 2015

In ZendExpressive, middleware_pipeline is a config that can be used to seed pre- and/or post-routing middleware. There are two keys inside it: pre_routing and post_routing.
Each of them has different purpose, pre_routing means the middleware registered under it will always be pipe()‘d before routing middleware, and for post_routing, they will be pipe()‘d afterwards.

If we create middlewares on them, we may need a call $next that on 3rd parameter in __invoke() method in its middlewares or middlewares that uses them as “next” middlewares. The code may be like this:

public function __invoke($request, $response, $next = null)
{
    // do something with request or response here
    return $next($request, $response);
}

$next will bring current $request and $response which may be modified before filled.

Ok, Let’s say we need an authorization middleware that pipe()‘d early, that means, we need a pre_routing:

class AuthorizationAction {

    public function __invoke($request, $response, $next = null)
    {
        // here, for example: check path and session exists
        // and handle with return response early or
        // continue to routing middleware
        return $next($request, $response);
    }

}

We can do something like this:

class AuthorizationAction {
  public function __construct($session) 
  { /** **/ }
    
  public function __invoke($request, $response, $next = null)
  {
    $path         = $request->getUri()->getPath();
    $sesionExists = $this->session->offsetExists('storage');

    // redirect to login page
    // when path = '/admin' and  
    // doesn't has session 
    if ($path === '/admin' && ! $sesionExists) {
        return new RedirectResponse('/auth');
    }

    if ($path === '/admin' && $sesionExists) {
        // check if session storage value !== 'admin'
        // then return early with status = 403
        $storageValue = $this->session->offsetGet('storage');
        if ($storageValue !== 'admin') {
            $response = $response->withStatus(403);
            $response->write('Forbidden');

            return $response;
        }
    }

    // if everything ok, continue
    return $next($request, $response);
  }
}

We then can register in our 'middleware_pipeline' config:

'middleware_pipeline' => [
    'pre_routing' => [
        [
            'middleware' => 'App\Action\AuthorizationAction',
        ],
    ],
    'post_routing' => [
        // ...
    ],
],

For example, we have routed middleware:

'routes' => [
    [
        'name' => 'funny',
        'path' => '/funny',
        'middleware' => App\Action\FunnyPageAction::class,
        'allowed_methods' => ['GET'],
    ],
    [
        'name' => 'admin',
        'path' => '/admin',
        'middleware' => App\Action\AdminPageAction::class,
        'allowed_methods' => ['GET'],
    ],
],

The AuthorizationAction middleware above will be pipe()‘d before middleware that registered in routes when specified middleware with its route match called.

So, how about post_routing ? We can call middleware registered in it, only if desired, that means, only if $next called in middleware that uses that. For example, we have a SugarAction middleware that change response header that will be placed in post_routing:

class SugarAction {

  public function __construct(RouterInterface $router)
  { $this->router = $router;}

  public function __invoke($request, $response, $next = null)
  {
     if (! $this->router->match($request)->isFailure()) {
         $response = $response->withHeader('X-Powered-By', 'JAVA');
     }
     return $next($request, $response);
  }
}

For note: As post_routing, because if the route is not match, the middleware above will be fall called, and withHeader() actually create new Response instance, we need to check whenever route match is not failure.

We then can register in our ‘middleware_pipeline’ config:

'middleware_pipeline' => [
    'pre_routing' => [
        [
            'middleware' => 'App\Action\AuthorizationAction',
        ],
    ],
    'post_routing' => [
        [
            'middleware' => 'App\Action\SugarAction',
        ],
    ],
],

and we have a /funny that uses that, so we can call $next:

class FunnyAction { 
  public function __invoke($request, $response, $next = null) {
    $response =  $response->write(
        $this->template->render('app::funny-page')
    );
    return $next($request, $response);
  }
}

If we check the header ( for example with Firebug or RestClient), and we can see the header ‘X-Powered-By’ changed. If we have a “not funny page”, and don’t want to modify header, then we can just not use $next.

Using Router match Check and Diactoros’s RedirectResponse in Zend\Expressive

Posted in Tutorial PHP, Zend Framework by samsonasik on September 17, 2015

If we use Zend\Expressive middleware, and want to handle redirection using Diactoros’s RedirectResponse, we may need to check whenever the Uri that we want to redirect is a valid and match against registered Router, if not valid, then back to home/other handling. Zend\Expressive gives us a choice to use ZF2 Router, Aura Router, or FastRoute. They have a way to check Uri is on registered Router, and they return different value when not valid (null for ZF2 Router, false for Aura Router, and FastRoute check with Dispatcher FOUND and NOT_FOUND constants). Zend\Expressive facilitating it by provide Router ‘adapter’ for each of them, and they have match method that return RouteResult instance as Value Object. They RouteResult has a $success property that will be setted as false when not match, and true when matched via following functions:

namespace Zend\Expressive\Router;

class RouteResult
{
    // (new self())->success = true;
    public static function fromRouteMatch($name, $middleware, array $params){}
    // (new self())->success = false;
    public static function fromRouteFailure($methods = null){} 
}

We have another methods to check $success property that have setted:

namespace Zend\Expressive\Router;

class RouteResult
{
    // return $this->success
    public static function isSuccess(){}
    // return (! $this->success);
    public static function isFailure(){} 
}

We can use one of the methods above to check $success property value.

Ok, let’s create an example! For example, we have a SamplePageAction as middleware:

namespace App\Action;

use Zend\Diactoros\Response\RedirectResponse;

class SamplePageAction
{
    public function __invoke($request, $response, $next = null)
    {
        return new RedirectResponse('/a-sample-uri');
    }
}

We need to ‘validate’ if ‘/a-sample-uri’ is a valid uri for our Zend\Expressive application. We can do:

  1. Inject the middleware with Zend\Expressive\Router\RouterInterface
    class SamplePageAction
    {
        private $router;
    
        public function __construct(RouterInterface $router)
        {
            $this->router = $router;
        }
    
        public function __invoke($request, $response, $next = null)
        { /** **/ }
    }
    

  2. Make factory for it

    namespace App\Action;
    
    use Interop\Container\ContainerInterface;
    use Zend\Expressive\Router\RouterInterface;
    
    class SamplePageActionFactory
    {
        public function __invoke(ContainerInterface $container)
        {
            $router   = $container->get(RouterInterface::class);
            return new SamplePageAction($router);
        }
    }
    

  3. If we are using Zend\ServiceManager as the Container, we can register inside our config at ‘factories’

    use App\Action\SamplePageAction;
    use App\Action\SamplePageActionFactory;
    
    //...
            'factories' => [
                SamplePageAction::class => SamplePageActionFactory::class,
            ]
    // ...
    

    if we use other container, we can follow the docs.

  4. Now, we can use it to check and handle route match the request uri

    class SamplePageAction
    {
        public function __construct(RouterInterface $router){ /** **/ }
        public function __invoke($request, $response, $next = null)
        {
            $uri         =  '/a-sample-uri';
    
            // save current path as variable
            $currentPath = $request->getUri()->getPath();
            // set request with Uri
            $request     = $request->withUri(new Uri($uri));
    
            // check match
            $match = $this->router->match($request);
            // check failure and if request uri path is not same with current
            // path to avoid infinite redirection 
            if (! $match->isFailure() 
                && $currentPath !== $request->getUri()->getPath()
            ) {   
                return new RedirectResponse($uri);
            }
    
            // redirect to uri with router name = home 
            // when router is not match  
            return new RedirectResponse($this->router->generateUri('home'));
        }
    }
    

    In real world app, the redirect check usually come from a variable, for example, a Query param(for ex: ‘redirect’ query param). For that case, we can do check like this:

    class SamplePageAction
    {
        public function __construct(RouterInterface $router){ /** **/ }
        public function __invoke($request, $response, $next = null)
        {
            // get arrays of Query params
            $queryParams = $request->getQueryParams();
            
            // check if 'redirect' Query param is set
            // and not equals than ''
            if (isset($queryParams['redirect']) 
                && trim($queryParams['redirect']) !== ''
            ) {  
                // save current path as variable
                $currentPath = $request->getUri()->getPath();
                // set request with Uri
                $request = $request->withUri(new Uri($queryParams['redirect']));
                 
                 // check match
                 $match = $this->router->match($request);
                 // check failure and if request uri path is not same with
                 // current path to avoid infinite redirection 
                 if (! $match->isFailure() 
                     && $currentPath !== $request->getUri()->getPath()
                 ) {
                     return new RedirectResponse($queryParams['redirect']);
                 }
            }
    
            return new RedirectResponse($this->router->generateUri('home'));
        }
    }
    

Update: There is a package for that that I created, go grab it here: https://github.com/samsonasik/ExpressiveRedirectHandler😉

Practical GIT (4) : Rebasing Conflicted “task” branch against primary branch

Posted in GIT by samsonasik on September 16, 2015

git-logoWhen we work on our “task” branch that based on our primary branch, We may have overlapped commits when our team have update the primary branch.
Many people just use “merge” the primary branch into their “task” branch, but it will make extra commit that contains actually same changes, that may a project history not clean, and make hard to review (for example: in case we use merge or pull request). What we need to do is rebase instead. Actually, the merge should only happen when you need to apply your “task” branch into your primary branch.

Rebase will synchronize the commits and put our changes in top of commits. Let’s see what the process step by step. For example, you work with git flow, you’re now on “feature/xzy” against develop.

$ git flow feature start xzy
# can be replaced with 
# git checkout -b feature/xzy develop

And we are working on a big task. On the middle of the day, someone or some people on the team make a hotfix that make a commit to master and develop, that make your branch is outdate, and has some conflict.
What you can do to synchronize is by using rebase with steps:

1. Checkout primary branch (at this case: develop), pull latest

$ git checkout develop
$ git pull origin develop

2. Back to our feature branch and rebase

$ git checkout feature/xzy
$ git rebase develop

From now on, if we see conflict, and get error:

You are currently rebasing.
  (fix conflicts and then run "git rebase --continue")

We need to see the files that contains conflicts that may contains:

<<<<<<< HEAD

, and fix them. After that, we can close the editor. Don’t make a commit after fixing conflicts in the files!

3. Add resolved files and you can continue

$ git add .
$ git rebase --continue

4. Last, if we publish the “feature/xzy” branch into our origin/feature/xyz, we need to push –force because the commits has been rewritten:

$ git push --force origin feature/xzy

If everything ok, then we are now synchronized. That’s it😉

Getting Closer with Penny – A PHP Framework

Posted in Tutorial PHP by samsonasik on September 8, 2015

“One penny is valueless but a lot of pennies build an empire.”. This is a tagline that we can see at the Penny repository. Gianluca Arbezzano, A PHP developer that created it focused on creating interoperability concept. It uses great PHP components in the Framework.

There are PHP components that used in Penny Framework:
* nikic/fast-route: handle routing
* php-di/php-di: act as service container
* zendframework/zend-diactoros: a PHP package implements psr-7
* zendframework/zend-eventmanager: handle application event
* doctrine/annotations:for annotation usage
* zendframework/zend-stdlib: used to manage configs.

Penny ships Skeleton Application to get started, we can follow the installation steps, and we can see the welcome page.

The Penny’s Skeleton page brings Plates to manage template, so you don’t need to worry about adding manually. The template configureable in the skeleton’s config:

// config/di.php
return [
    'template' => \DI\object(\League\Plates\Engine::class)->constructor('./app/view/'),
    'router' => /* */
];

And about the routing, it brings the basic FastRoute‘s config:

// config/di.php
return [
    'template' => /* */
    'router' => function () {
        return \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
            $r->addRoute('GET', '/', ['ClassicApp\Controller\IndexController', 'index']);
        });
    },
];

In ClassicApp\Controller\IndexController::index, we have Stream object that write with rendered template:

// app/Controller/IndexController.php
    public function index($request, $response)
    {
        $response->getBody()->write($this->template->render('index', [
            'title' => 'Home Page',
        ]));
        return $response;
    }

The template property in the controller above injected via @Inject annotation:

// app/Controller/IndexController.php
class IndexController {
    /**
     * @Inject("di")
     */
    private $di;

    /**
     * @Inject("template")
     */
    private $template;
}

And you’re already ready to go. But, what if You don’t like the way it worked, You need __construct injection? There we go:

// app/Controller/IndexController.php
use League\Plates\Engine as PlatesEngine;

class IndexController
{
    public function __construct(PlatesEngine $template)
    {
        $this->template = $template;
    }
}

And we can re-configure the config/di.php:

// config/di.php
use ClassicApp\Controller\IndexController;

return [

    IndexController::class => \DI\object(IndexController::class)->constructor(\DI\get('template')),

    'template' => /* */
    'router' => /* */
];

You want another container instead of php-di ? You can! The GianArb\Penny\App allows passing $container in 2nd parameter, so you can use it in public/index.php:

// public/index.php
$app = new \GianArb\Penny\App(null, $yourChoiceContainer);

Penny use Zend\EventManager to manage application event, There are 3 events that may be triggered:
* ERROR_DISPATCH: when error happen ( route not found, method not allowed, etc)
* filename.function: runnable
* filename.function_error: runnable but exception found.

Interesting? Take a look more on the site! Curious about the code behind it? Consult the code😉.

Follow

Get every new post delivered to your Inbox.

Join 351 other followers