Welcome to Abdul Malik Ikhsan's Blog

Apigility: Create custom Authentication for Oauth2 with service delegators

Posted in Tutorial PHP, Zend Framework by samsonasik on August 21, 2016

apigility logo Custom authentication in apigility is do-able with service delegators. We need to wrap ZF\MvcAuth\Authentication\DefaultAuthenticationListener::class in decorator. For example, we want to use ZF\OAuth2\Adapter\PdoAdapter but want to modify checkUserCredentials($username, $password) to include is_active check. Let’s do it!

  1. Setup Apigility Authentication with Oauth2
  2. With in assumption, we have the following config:
    return [
    // ... config/autoload/local.php
        'zf-oauth2' => [
          'db' => [
              'driver' => 'PDO_Mysql',
              'username' => 'root',
              'password' => '',
              'dsn' => 'mysql:host=localhost;dbname=app_oauth',
          ],
        ],
    // ...
    ];
    

    We can then modify config/autoload/zf-mvc-auth-oauth2-override.global.php as follows:

    // config/autoload/zf-mvc-auth-oauth2-override.global.php
    return [
        'service_manager' => [
            'factories' => [
                'ZF\OAuth2\Service\OAuth2Server' 
                    => 'Application\MvcAuth\NamedOAuth2ServerFactory',
            ],
        ],
    ];
    
  3. Define our own NamedOAuth2ServerFactory to use our own OAuth2ServerFactory for OAuth2\Server instance creation
    namespace Application\MvcAuth;
    
    use Interop\Container\ContainerInterface;
    
    class NamedOAuth2ServerFactory
    {
        /**
         * @param ContainerInterface $container
         *
         * @return callable
         */
        public function __invoke(ContainerInterface $container)
        {
            $config = $container->get('config');
    
            $oauth2Config = isset($config['zf-oauth2']) ? $config['zf-oauth2'] : [];
            $mvcAuthConfig = isset($config['zf-mvc-auth']['authentication']['adapters'])
                ? $config['zf-mvc-auth']['authentication']['adapters']
                : [];
    
            $servers = (object) ['application' => null, 'api' => []];
    
            return function ($type = null) use (
               $oauth2Config, $mvcAuthConfig, $container, $servers
            ) {
                foreach ($mvcAuthConfig as $name => $adapterConfig) {
                    if (!isset($adapterConfig['storage']['route'])) {
                        // Not a zf-oauth2 config
                        continue;
                    }
    
                    if ($type !== $adapterConfig['storage']['route']) {
                        continue;
                    }
    
                    // Found!
                    return $servers->api[$type] = OAuth2ServerFactory::factory(
                        $adapterConfig['storage'],
                        $container
                    );
                }
            };
        }
    }
    
  4. Create our Application\MvcAuth\OAuth2ServerFactory based on \ZF\MvcAuth\Factory\OAuth2ServerFactory
    namespace Application\MvcAuth;
    
    use Interop\Container\ContainerInterface;
    use OAuth2\GrantType\AuthorizationCode;
    use OAuth2\GrantType\ClientCredentials;
    use OAuth2\GrantType\RefreshToken;
    use OAuth2\GrantType\UserCredentials;
    use OAuth2\GrantType\JwtBearer;
    use OAuth2\Server as OAuth2Server;
    
    final class OAuth2ServerFactory
    {
        private function __construct()
        {
        }
    
        public static function factory(array $config, ContainerInterface $container)
        {
            $allConfig = $container->get('config');
            $oauth2Config = isset($allConfig['zf-oauth2']) ? $allConfig['zf-oauth2'] : [];
            $options = self::marshalOptions($oauth2Config);
    
            $oauth2Server = new OAuth2Server(
                $container->get(\ZF\OAuth2\Adapter\PdoAdapter::class),
                $options
            );
    
            return self::injectGrantTypes($oauth2Server, $oauth2Config['grant_types'], $options);
        }
    
       private static function marshalOptions(array $config)
       { 
           // same as \ZF\MvcAuth\Factory\OAuth2ServerFactory::marshalOptions()
       }
       
        private static function injectGrantTypes(
           OAuth2Server $server,
           array $availableGrantTypes,
           array $options
       ) {
          // same as \ZF\MvcAuth\Factory\OAuth2ServerFactory::injectGrantTypes()
       }
    }
    
  5. As we want custom PdoAdapter, we need to map \ZF\OAuth2\Adapter\PdoAdapter::class to our PdoAdapter, for example: Application\MvcAuth\PdoAdapter:
    namespace Application\MvcAuth;
    
    use Zend\Crypt\Bcrypt;
    use ZF\OAuth2\Adapter\PdoAdapter as BasePdoAdapter;
    
    class PdoAdapter extends BasePdoAdapter
    {
        public function checkUserCredentials($username, $password)
        {
            $stmt = $this->db->prepare(
                'SELECT * from oauth_users where username = :username and is_active = 1'
            );
            $stmt->execute(compact('username'));
            $result = $stmt->fetch();
    
            if ($result === null) {
                return false;
            }
    
            // bcrypt verify
            return $this->verifyHash($password, $result['password']);
        }
    }
    
  6. For our Application\MvcAuth\PdoAdapter, we need to define factory for it:
    namespace Application\MvcAuth;
    
    use Interop\Container\ContainerInterface;
    use ZF\OAuth2\Factory\PdoAdapterFactory as BasePdoAdapterFactory;
    
    class PdoAdapterFactory extends BasePdoAdapterFactory
    {
        public function __invoke(ContainerInterface $container)
        {
            $config = $container->get('config');
    
            return new PdoAdapter([
                'dsn' => $config['zf-oauth2']['db']['dsn'],
                'username' => $config['zf-oauth2']['db']['username'],
                'password' => $config['zf-oauth2']['db']['password'],
                'options' => [],
            ], []);
        }
    }
    
  7. Register the adapter into service manager into config/autoload/global.php
    // config/autoload/global.php
    return [
    // ... 
        'service_manager' => [
            'factories' => [
                \ZF\OAuth2\Adapter\PdoAdapter::class => 
                     \Application\MvcAuth\PdoAdapterFactory::class,
            ],
        ],
    // ...
    ];
    

  8. Time to attach the \ZF\OAuth2\Adapter\PdoAdapter into our delegated service ZF\MvcAuth\Authentication\DefaultAuthenticationListener via delegator factory

    namespace Application\MvcAuth;
    
    use Interop\Container\ContainerInterface;
    use OAuth2\Server as OAuth2Server;
    use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
    use ZF\MvcAuth\Authentication\OAuth2Adapter;
    
    class AuthenticationListenerDelegatorFactory implements DelegatorFactoryInterface
    {
        public function __invoke(
           ContainerInterface $container,
           $name,
           callable $callback,
           array $options = null
       ) {
            $listener = call_user_func($callback);
            $listener->attach(
                new OAuth2Adapter(
                    new Oauth2Server(
                        $container->get(\ZF\OAuth2\Adapter\PdoAdapter::class),
                        ['Bearer']
                    )
                )
            );
    
            return $listener;
        }
    }
    

  9. Last one! Register our AuthenticationListenerDelegatorFactory into service delegators:

    // config/autoload/global.php
    return [
    // ... 
        'service_manager' => [
            'delegators' => [
                \ZF\MvcAuth\Authentication\DefaultAuthenticationListener::class => [
                    \Application\MvcAuth\AuthenticationListenerDelegatorFactory::class
                ],
            ],
        ],
    // ...
    ];
    

Done๐Ÿ˜‰

Using Middleware in CakePHP “3.next”

Posted in CakePHP by samsonasik on May 29, 2016

Middleware support will be come in CakePHP 3.3.0, but you can try it now, there is a branch for it named “3.next”! If you want to start with composer create-project, you can run with the following command:

composer create-project --prefer-dist cakephp/app <dir-target> dev-3.next

By specifying “dev-3.next” after directory target, you will pull the 3.next branch of “cakephp/app”. Once it done, you will now have an Application class inside src/ that bootstrap the application and also setup the middleware your application will use:

namespace App;

class Application extends BaseApplication
{
    public function bootstrap(){ /** **/}

    public function middleware($middleware)
    {
        $middleware->push(new ErrorHandlerMiddleware());
        $middleware->push(new AssetMiddleware());
        $middleware->push(new RoutingMiddleware());

        return $middleware;
    }
}

Now, let say you want to add your own middleware, named “Authorization” middleware, let’s create it:

namespace App\Middleware;

use Cake\Network\Session;
use Cake\Core\Configure;
use Zend\Diactoros\Response\RedirectResponse;

class Authorization
{
    public function __invoke($request, $response, $next)
    {
        $session             =  Session::create(Configure::read('Session'));
        $checkHasUserSession = $session->check('user');

        $path = $request->getUri()->getPath();
        if ($path === '/admin' && ! $checkHasUserSession) {
            return new RedirectResponse('/auth');
        }

        return $next($request, $response);
    }
}

The “Authorization” middleware we just created now needs to be registered via middleware->push:

namespace App;

use App\Middleware\Authorization;

class Application extends BaseApplication
{
    public function middleware($middleware)
    {
        $middleware->push(new ErrorHandlerMiddleware());
        $middleware->push(new AssetMiddleware());
        $middleware->push(new RoutingMiddleware());
        
        //add the Authorization middleware
        $middleware->push(new Authorization());

        return $middleware;
    }
}

Done๐Ÿ˜‰

References:
1. http://www.slideshare.net/markstory/future-of-http-in-cakephp

Tagged with: ,

Using ZF Component’s ConfigProvider in Expressive

Posted in Tutorial PHP, Zend Framework by samsonasik on May 24, 2016

If you already tried building Expressive application using modular approach with Expressive Config Manager, you can just use ZF Component’s services by consuming its config provider. If you didn’t use it, you need to first require the Expressive Config Manager in your Expressive application:

composer require mtymek/expressive-config-manager

When done, you can modify the config/config.php like the following:

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

$configManager = new ConfigManager([
    new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
]);

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

Now, for example, you want to consume Zend\Db services, you can do:

1. Require it

 composer require zendframework/zend-db

2. Register in config/config.php

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

$configManager = new ConfigManager([
    new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
    new Zend\Db\ConfigProvider()
]);

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

By provide Zend\Db Config Provider, you can now consume its service, let’s try define sample DB config:

// config/autoload/db.local.php
return [
    'db' => [
        'username' => 'root',
        'password' => '',

        'driver' => 'Pdo',
        'dsn' => 'mysql:dbname=test;host=localhost',
        'driver_options' => [
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        ],
    ],
];

And now, you can consume \Zend\Db\Adapter\AdapterInterface::class as service. Check the per-component’s ConfigProvider::getDependencyConfig() for available services if you want to use them.

Done๐Ÿ˜‰

Zend Framework 2 : Using Component as Module

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 17, 2016

If you use latest zendframework by requiring “zendframework/zendframework”: “^2.5”, You will now get various zendframework components tagged as ~2.7 or ~2.8 as per-component has own life based on each evolution. Interestingly, many components that has services, now act as module. You can check the components on zendframework blog post. In the components mentioned in the post, they will have Module class that uses ConfigProvider class when registering services. If you don’t get the Module class yet inside the components mentioned, you need to run:

composer update

Usage
We can consume the components as modules, for example, in config/application.config.php, you can define:

return [
    'modules' => [
        'Application',
        'Zend\Cache'
    ],
    //  other configs here 
];

If you check the services registered in Zend\Cache, you will get Zend\Cache\Service\StorageCacheAbstractServiceFactory::class that registered in abstract_factories, that allow us to configure cache services via array with ‘caches’ config as limbo when no services that has name inside the ‘caches’ array registered yet. So, for example, we have configured ‘caches’ config:

// config/autoload/global.php
return [
    // ...
    'caches' => [
        'cache_file' => [
            'adapter' => 'filesystem',
            'lifetime' => 7200,
            'options' => [
                'cache_dir' => './data/cache',
            ],
        ],
    ],
];

We have cache_file as service name that utilize filesystem cache adapter as config above. We then can just consume the cache_file via service manager:

// assumption: $services is a Zend\ServiceManager\ServiceManager 
// that pulled during Mvc workflow
$services->get('cache_file')->setItem('foo', 'fooValue');
echo $services->get('cache_file')->getItem('foo');

Done๐Ÿ˜‰

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.