Replace Hard Dependency Class with New Simulation Class via “replace” and “classmap” in Composer Configuration
If we are using 3rd party library that managed by composer, which has hard dependency that we don’t want to use, for example, at the following use case:
- “realexpayments/rxp-remote-php” library require a “apache/log4php” dependency for apache “Logger” class usage.
- we don’t want to log everything via “apache/log4php”‘s Logger, for whatever reason.
The one of the solutions for that is by using “replace” and “classmap” configuration in our composer.json
. First, we need to prepare of the class to simulate the Logger
class, for example, we have it in src/App/Apache/Logger.php
:
<?php // src/App/Apache/Logger.php class Logger { function debug(...$args) {} function info(...$args) {} function trace(...$args) {} function warn(...$args) {} function error(...$args) {} function fatal(...$args) {} public static function configure(...$args) {} public static function getLogger() { return new self(); } }
Yes, above class doesn’t do anything, for silent action when Logger::{themethod()}
called in realexpayments/rxp-remote-php
library classes.
Next, we can register it to our composer.json:
{ "require": { // ... "realexpayments/rxp-remote-php": "^1.2" // ... }, "replace": { "apache/log4php": "^2.3.0" }, "autoload": { "psr-4": { "App\\": "src/App/", }, "classmap": [ "src/App/Apache/Logger.php" ] } }
In above configuration, the replace
of “apache/log4php” doesn’t has replacement library in ‘require’ part will make the dependency removed entirely as we don’t want to use it anymore, and by the classmap
configuration, we have new redefined of the Logger class as simulation of “apache/log4php” Logger class.
Last step, we can run:
➜ composer update
That’s it!
Using zend-expressive-session-cache as PSR-6 session persistence adapter in Expressive 3
zend-expressive-session-cache is a PSR-6 session persistence adapter for zend-expressive-session that can be used in Expressive 3. In current post, I will provide the use case of it with apcu for cache adapter with zend-cache for cache item pool.
Let’s start with create new project from skeleton with the following command:
➜ composer create-project \ zendframework/zend-expressive-skeleton \ expressive3-cache-tutorial
After the skeleton installed, we can require the zendframework/zend-expressive-session-cache
and zend-cache
with the following command:
➜ cd expressive3-cache-tutorial ➜ composer require \ zendframework/zend-expressive-session-cache \ zendframework/zend-cache
Now, ensure config/config.php
has the following ConfigProvider
s registered:
<?php // config/config.php $aggregator = new ConfigAggregator([ // ... \Zend\Cache\ConfigProvider::class, \Zend\Expressive\Session\Cache\ConfigProvider::class, \Zend\Expressive\Session\ConfigProvider::class, // ... ], $cacheConfig['config_cache_path']);
For apcu
adapter, we need the apcu
extension to be installed, we can install via pecl
like the following:
➜ sudo pecl install apcu
After it installed, we need to set apc.use_request_time = 0
in php.ini like the following:
# your php.ini apc.use_request_time = 0
For Cache Item Pool service, we can create a factory for it, for example, like the following:
<?php // src/App/Cache/CacheItemPoolFactory.php declare(strict_types=1); namespace App\Cache; use Psr\Cache\CacheItemPoolInterface; use Psr\Container\ContainerInterface; use Zend\Cache\Psr\CacheItemPool\CacheItemPoolDecorator; use Zend\Cache\StorageFactory; final class CacheItemPoolFactory { public function __invoke(ContainerInterface $container) : CacheItemPoolInterface { $storage = StorageFactory::factory([ 'adapter' => [ 'name' => 'apcu', ], ]); return new CacheItemPoolDecorator($storage); } }
We can register then the cache pool service and make alias of SessionPersistenceInterface
with CacheSessionPersistence
:
<?php // config/autoload/dependencies.global.php use App\Cache\CacheItemPoolFactory; use Psr\Cache\CacheItemPoolInterface; use Zend\Expressive\Session\Cache\CacheSessionPersistence; use Zend\Expressive\Session\SessionPersistenceInterface; return [ // ... 'aliases' => [ SessionPersistenceInterface::class => CacheSessionPersistence::class, ], 'factories' => [ CacheItemPoolInterface::class => CacheItemPoolFactory::class, ], // ... ];
If we need a custom configuration for zend-expressive-session-cache
, we can define at config/autoload/zend-expressive.global.php
:
<?php // config/autoload/zend-expressive.global.php use Psr\Cache\CacheItemPoolInterface; return [ // ... 'zend-expressive-session-cache' => [ 'cache_item_pool_service' => CacheItemPoolInterface::class, 'cookie_name' => 'PHPSESSION', 'cookie_path' => '/', 'cache_limiter' => 'nocache', 'cache_expire' => 10800, 'last_modified' => null, ], // ... ];
To start the session, we can apply Zend\Expressive\Session\SessionMiddleware
into config/pipeline.php
before Zend\Expressive\Router\Middleware\RouteMiddleware
registration:
<?php use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Session\SessionMiddleware; // ... $app->pipe(SessionMiddleware::class); $app->pipe(RouteMiddleware::class); // ...
The preparation done! Now, we can use it by consume it from via SessionMiddleware, for example, we need to generate “csrf” token when rendering the form, like I written at the CSRF usage in Expressive post, we can just use the session as is, and the cache will be used as session persistence:
<?php // src/Handler/LoginPageHandler.php use Zend\Expressive\Session\SessionInterface; use Zend\Expressive\Csrf\SessionCsrfGuard; use Zend\Expressive\Session\SessionMiddleware; use Zend\Expressive\Csrf\CsrfMiddleware; // ... private function getToken(SessionInterface $session, SessionCsrfGuard $guard) { if (! $session->has('__csrf')) { return $guard->generateToken(); } return $session->get('__csrf'); } public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $guard = $request->getAttribute(CsrfMiddleware::GUARD_ATTRIBUTE); $loginForm = new LoginForm($guard); $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); $token = $this->getToken($session, $guard); // ... } // ...
That’s it!
Using “no-api” key on Github Forked Repository in Composer repositories configuration for Travis Build
So, you’re doing a pull request, you want the feature to be merged so bad to public repository, but for whatever reason, it is not merged yet. You can register your forked repository under composer “repositories” config:
{ "require": { "awesome/library": "dev-my-fork-awesome-feature" }, "repositories" : [ { "type" : "vcs", "url" : "https://github.com/yourgithubuser/library" } ] }
That will work for your local dev and your server, but unfortunatelly, that won’t work on Travis! We will get the following error:
Failed to clone the git@github.com:yourgithubuser/library.git repository, try running in interactive mode so that you can enter your GitHub credentials [RuntimeException] Failed to execute git clone --mirror 'git@github.com:yourgithubuser/library.git' '/home/travis/.composer/cache/vcs/git-github.com-yourgithubuser-library.git/'
To make that work, we will need the “no-api” key in your composer.json under the per-repository inside “repositories”, as follow:
// ... { "type" : "vcs", "url" : "https://github.com/yourgithubuser/library", "no-api": true } // ...
Now, our composer.json will look like this:
{ "require": { "awesome/library": "dev-my-fork-awesome-feature" }, "repositories" : [ { "type" : "vcs", "url" : "https://github.com/yourgithubuser/library", "no-api": true } ] }
That’s it!
leave a comment