Welcome to Abdul Malik Ikhsan's Blog

Create ZF Client Authentication for Apigility Oauth with ApigilityConsumer

Posted in Tutorial PHP, Zend Framework, Zend Framework 2, Zend Framework 3 by samsonasik on March 28, 2017

If you have Apigility as API builder in API side, and client app that consume it using Zend Framework 2/3 or ZF Expressive, you can create an authentication from the client application that call oauth in apigility side.

Zend\Authentication has AbstractAdapter that you can extends to create custom adapter for its need. Let’s assume the applications are like the following diagram:

[CLIENT - A ZF Application]              [API - An Apigility Application]
         |                                          |
   AuthController                     ZF\MvcAuth\Authentication\OAuth2Adapter          
         |                                          |
         |       authenticateAction()               |
         |   ------------------------------------>  |
         |         identity json                    |
         |   <------------------------------------  |

On oauth result call, you may get the following result:

{
  "access_token": "8e4b0e5ddc874a6f1500514ef530dbea3976ae77",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": null,
  "refresh_token": "d19b79cd376924409c14ee46e5230617482fb169"
}

The ApigilityConsumer

ApigilityConsumer is a ZF2/ZF3 Apigility Client module (can also be used in ZF Expressive) to consume Apigility API Services.

You can install by run composer command:

composer require samsonasik/apigility-consumer

For full configurations and features, you can read at its README, for this post’s need, you can do something like this:

<?php
// config/autoload/apigility-consumer.local.php
return [
    'apigility-consumer' => [
        // your apigility host url
        'api-host-url' => 'https://your.apigilty.api.host',

        // your apigility oauth setting
        'oauth' => [

            'grant_type'    => 'password',
            'client_id'     => 'your client id',
            'client_secret' => 'your client secret',

        ],

    ],
];

and register the module into config/application.config.php or config/modules.config.php:

<?php
// config/application.config.php or config/modules.config.php
return [
    'ApigilityConsumer', // <-- register here
    'Application',
],

Create Adapter

You need to extends Zend\Authentication\Adapter\AbstractAdapter and implements Zend\Authentication\Adapter\AdapterInterface. So, You can have the class:

<?php

namespace Application\Adapter;

use ApigilityConsumer\Service\ClientAuthService;
use Zend\Authentication\Adapter\AbstractAdapter;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Authentication\Result;

class ApigilityAuthenticationAdapter
    extends AbstractAdapter
    implements AdapterInterface
{
    /**
     * @var ClientAuthService
     */
    private $clientAuthService;

    /**
     * @param  ClientAuthService $clientAuthService
     */
    public function __construct(ClientAuthService $clientAuthService)
    {
        $this->clientAuthService = $clientAuthService;
    }

    /**
     * @return Result
     */
    public function authenticate()
    {
        $clientResult = $this->clientAuthService->callAPI(
            [
                // your oauth registered route segment in apigility. 
                'api-route-segment' => '/oauth', 

                'form-data' => [
                    'username' => $this->getIdentity(),
                    'password' => $this->getCredential(),
                ],

                'form-request-method' => 'POST',
            ]
        );

        if (! $clientResult->success) {
            return new Result(Result::FAILURE, null, $clientResult::$messages);
        }

        return new Result(RESULT::SUCCESS, $clientResult->data);
    }
}

Your can now build a factory from it:

<?php
namespace Application\Adapter;

use ApigilityConsumer\Service\ClientAuthService;

class ApigilityAuthenticationAdapterFactory
{
    public function __invoke($container)
    {
        return new ApigilityAuthenticationAdapter(
            $container->get(ClientAuthService::class)
        );
    }
}

You can then register at service_manager:

<?php
// module/Application/config/module.config.php
namespace Application;

'service_manager' => [
    'factories' => [
        // ...
        Adapter\ApigilityAuthenticationAdapter::class => Adapter\ApigilityAuthenticationAdapterFactory::class,
    ],
],

For ZF Expressive, you can register under ‘dependencies’ key.

Set Adapter into AuthenticationService

You need to set authentication service’s adapter with defined adapter above with factory:

<?php
namespace Application\Factory;

use Application\Adapter\ApigilityAuthenticationAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage\Session;

class AuthenticationServiceFactory
{
    public function __invoke($container)
    {
        $adapter = $container->get(ApigilityAuthenticationAdapter::class);

        return new AuthenticationService(
            new Session(), // or your own storage implementing  Zend\Authentication\Storage\StorageInterface
            $adapter
        );
    }
}

You can then register also at service_manager:

<?php
// module/Application/config/module.config.php
namespace Application;

use Zend\Authentication\AuthenticationService;

'service_manager' => [
    'factories' => [
        // ...
        AuthenticationService::class => Factory\AuthenticationServiceFactory::class,
    ],
],

For ZF Expressive, you can register under ‘dependencies’ key.

The AuthController::authenticate()

I assume that you already inject controler with login form, use “username” and “password” as field names, and fill the data, so, your AuthController::authenticate() can be like the following:

<?php
namespace Application\Controller;

use Application\Form\LoginForm;
use Zend\Authentication\AuthenticationService;

class AuthController
{
    public function __construct(
        AuthenticationService $authenticationService,
        LoginForm $loginForm,
    ) { /* ...*/ }

    public function authenticateAction()
    {
        /*
         *    check request and form validity here
         */
        $formData = $this->loginForm->getData();
        $this->authenticationService->getAdapter()
                                    ->setIdentity($formData['username'])
                                    ->setCredential($formData['password']);

        $result = $this->authenticationService->authenticate();
        if (!$result->isValid()) {
            /**
             * For security reason, you should not show user the reason of failure,
             * However, if it actually needed for specific purpose, you can pull by call:
             *
             *     $result->getMessages();
             *
             */
            return $this->redirect()->toRoute('/auth');
        }

        return $this->redirect()->toRoute('/account');
    }
}

For ZF Expressive, you can create routed Authentication middleware.

That’s it, you’re now have successfully created a client authentication for your ZF2/ZF3 or ZF Expressive application that consume Apigility oauth.

Tagged with: , ,

Testing Zend Expressive 2 with kahlan 3

Posted in testing, Tutorial PHP, Zend Framework by samsonasik on March 15, 2017

Zend\Expressive ^2.0 has different default approach for piping and routing middleware which is programmatically way. In this post, I am going to show you how you can test Zend\Expressive ^2.0 application, with assumption, you use its skeleton with kahlan 3.

First, of course, install the Expressive ^2.0 skeleton, for example, install into directory named “expressive2”:

$ composer create-project zendframework/zend-expressive-skeleton:^2.0 expressive2
Installing zendframework/zend-expressive-skeleton (2.0.0)
  - Installing zendframework/zend-expressive-skeleton (2.0.0) Downloading: 100%
Created project in expressive2
> ExpressiveInstaller\OptionalPackages::install
Setting up optional packages
Setup data and cache dir
Removing installer development dependencies

  What type of installation would you like?
  [1] Minimal (no default middleware, templates, or assets; configuration only)
  [2] Flat (flat source code structure; default selection)
  [3] Modular (modular source code structure; recommended)
  Make your selection (2): 3

Now, install kahlan:

$ cd expressive2
$ composer require kahlan/kahlan:^3.1

We are going to need the $app variable inside tests, for example, when testing functionality for each routed middlewares. To simplify and avoid repetitive code, we can register it into kahlan-config.php in root application:

// ./kahlan-config.php
use Kahlan\Filter\Filter;
use Zend\Expressive\Application;

Filter::register('initialize app', function($chain) {

    $root = $this->suite();

    ob_start();

    $root->beforeAll(function ($var) {

        ob_start();

        $var->app = $app
                  = (require 'config/container.php')->get(Application::class);

        require 'config/pipeline.php';
        require 'config/routes.php';

    });

    return $chain->next();

});
Filter::apply($this, 'run', 'initialize app');

By assign $app into “$var->app” like above, the “$app” is accessible from entire tests via “$this->app”, so, we can write test like the following:

// ./src/App/spec/Action/HomePageActionSpec.php
namespace AppSpec\Action;

use Zend\Diactoros\ServerRequest;

describe('HomePageAction', function () {

    describe('/', function () {

        it('contains welcome message', function () {

            $serverRequest = new ServerRequest([], [], '/', 'GET');
            $this->app->run($serverRequest);
            $actual = ob_get_clean();

            expect($actual)->toContain('Welcome to <span class="zf-green">zend-expressive</span>');

        });

    });

});

Now, let’s run the tests:

$ vendor/bin/kahlan --spec=src/App/spec/
            _     _
  /\ /\__ _| |__ | | __ _ _ __
 / //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/  \/\__,_|_| |_|_|\__,_|_| | |

The PHP Test Framework for Freedom, Truth and Justice.

Working Directory: /Users/samsonasik/www/expressive2

.                                                                   1 / 1 (100%)



Expectations   : 1 Executed
Specifications : 0 Pending, 0 Excluded, 0 Skipped

Passed 1 of 1 PASS in 0.375 seconds (using 8Mo)

That’s it ๐Ÿ˜‰

Functional Test for Zend\Expressive Routed Middleware with Kahlan ^3.0

Posted in testing, Tutorial PHP, Zend Framework by samsonasik on January 13, 2017

You may tried do functional test Zend\Expressive Routed Middleware and end up with “Unable to emit response; headers already sent” error.
This can happen because of during run test, the Test framework itself already run fwrite() or echo to build test report, and make the headers_sent() return true.

To handle that, we can use ob_start(), but since the header is sent in the background, we need to place in both places:

  • test bootstrap
  • before each test

Seriously? Yes! That’s make sure we only get Diactoros response that we use in the buffer to be tested.

Preparation

As usual, we need require kahlan/kahlan:^3.0 in require-dev:

$ composer require --dev kahlan/kahlan:^3.0 --sort-packages

Set Kahlan’s Bootstrap and before each globally

In Kahlan, we can set tests bootstrap and what in all before each test with Kahlan\Filter\Filter in kahlan-config.php, so we can write:

<?php
//kahlan-config.php
use Kahlan\Filter\Filter;

ob_start();
Filter::register('ob_start at each', function($chain) {
    $root = $this->suite();
    $root->beforeEach(function () {
        ob_start();
    });
    return $chain->next();
});
Filter::apply($this, 'run', 'ob_start at each');

Write Spec and Run In Action

Now, if we use Expressive skeleton application, and for example, we need to test App\Action\PingAction routed middleware, we can write spec in spec directory:

.
โ”œโ”€โ”€ composer.json
โ”œโ”€โ”€ config
โ”œโ”€โ”€ data
โ”œโ”€โ”€ kahlan-config.php
โ”œโ”€โ”€ public
โ”œโ”€โ”€ spec
โ”‚ย ย  โ””โ”€โ”€ App
โ”‚ย ย      โ””โ”€โ”€ Action
โ”‚ย ย          โ”œโ”€โ”€ PingActionDispatchSpec.php
โ”œโ”€โ”€ src
โ”‚ย ย  โ””โ”€โ”€ App
โ”‚ย ย      โ””โ”€โ”€ Action
โ”‚ย ย          โ”œโ”€โ”€ PingAction.php

As the App\Ping\PingAction is return Zend\Diactoros\Response\JsonResponse which contains “ack” data with time() method call:

return new JsonResponse(['ack' => time()]);

The spec can be the following:

<?php
namespace AppSpec\Action;

use Zend\Diactoros\ServerRequest;
use Zend\Expressive\Application;

describe('PingAction Dispatch', function () {

    beforeAll(function() {
        $container = require 'config/container.php';
        $this->app = $container->get(Application::class);
    });

    describe('/api/ping', function () {

        it('contains json "ack" data', function () {

            allow('time')->toBeCalled()->andReturn('1484291901');

            $serverRequest = new ServerRequest([], [], '/api/ping', 'GET');
            $this->app->run($serverRequest);

            $actual = ob_get_clean();
            expect($actual)->toBe('{"ack":"1484291901"}');

        });

    });

});

The ob_start() will automatically called during test bootstrap and before each test.

Now, we can run the test:

$ vendor/bin/kahlan --coverage=4 --src=src/App/Action/PingAction.php 
            _     _
  /\ /\__ _| |__ | | __ _ _ __
 / //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/  \/\__,_|_| |_|_|\__,_|_| | |

The PHP Test Framework for Freedom, Truth and Justice.

Working Directory: /Users/samsonasik/www/expressive

.                                                                   1 / 1 (100%)



Expectations   : 1 Executed
Specifications : 0 Pending, 0 Excluded, 0 Skipped

Passed 1 of 1 PASS in 0.210 seconds (using 7Mo)

Coverage Summary
----------------
                                        Lines           %

 \                                      1 / 1     100.00%
โ””โ”€โ”€ App\                                1 / 1     100.00%
ย ย ย โ””โ”€โ”€ Action\                          1 / 1     100.00%
ย ย ย ย ย ย โ””โ”€โ”€ PingAction                    1 / 1     100.00%
ย ย ย ย ย ย ย ย ย โ””โ”€โ”€ PingAction::__invoke()     1 / 1     100.00%

Total: 100.00% (1/1)

Coverage collected in 0.003 seconds (using an additionnal 0o)

Done ๐Ÿ˜‰

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 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 ๐Ÿ˜‰

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

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 ๐Ÿ˜‰

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 ๐Ÿ˜‰

Zend Framework 2 : Using Zend Framework 1 libraries

Posted in Tutorial PHP, Zend Framework, Zend Framework 2 by samsonasik on December 4, 2012

zf2-zendframework2 Do you still need some components of Zend Framework 1 in your Project that use Zend Framework 2 ? It easy to use Zend Framework 1 libraries in Zend Framework 2 by register Zend Framework 1 libraries in Zend Framework 2 StandardAutoloader or by register at composer’s ClassLoader.

Continue Reading

Zend Framework 2 : Handling Multiple Database

Posted in Teknologi, Tutorial PHP, Zend Framework, Zend Framework 2 by samsonasik on June 6, 2012

Pada skala aplikasi yang besar, kita sering menjumpai aplikasi yang menggunakan beberapa database sekaligus. Zend Framework 2 memfasilitasi kebutuhan ini dengan settingan yang super flexible. Cukup memanggil array konfigurasi, tak perlu pusing memikirkan sintax sql yang mungkin berbeda di teknologi database lain, cukup configure, and run!

Baca Selengkapnya

Zend Framework : Zend_Oauth and Zend_Service_Twitter – Access Your Twitter Application

Posted in Zend Framework by samsonasik on December 12, 2011

Oauth, adalah sebuah protokol terbuka yang memungkinkan pengguna berbagi sumber pribadi yang disimpan di situs web dengan situs lain tanpa perlu menyerahkan username dan kata sandi. Proses ini diberikan dengan memberikan token. Setiap token memberikan akses spesifik terhadap resource spesifik.

Zend Framework mempunyai komponen hebat, bernama Zend_Oauth yang bisa digunakan sebagai consumer untuk me-request token yang dibutuhkan dengan passing konfigurasi callbackUrl, siteUrl, consumerKey, dan consumerSecret.

Baca Selengkapnya

Zend Framework : Zend_Search_Lucene – Content Indexing

Posted in Teknologi, Tutorial PHP, Zend Framework by samsonasik on August 8, 2011

Lucene adalah indexing dan retrieval library yang awalnya didevelop di teknologi Java, dan disupport oleh Apache Software Foundation. Ketika data sudah terindex dalam sistem file, maka tidak membutuhkan database server. Zend_Search_Lucene adalah salah satu komponen dari Zend Framework yang mengimplementasikan teknologi ini.ย  Zend_Search_Lucene support beberapa fitur :

1. Perangkingan hasil pencarian
2. Powerful query types : boolean, wildcard, phrase queries
3. pencarian dengan field spesifik

Baca Selengkapnya