Core Concepts
Drivers
Drivers are modular components that provide specific functionality to your Kipchak application, such as database access, cache management, or third-party service integrations.
What is a Driver?
In Kipchak, a driver is a reusable component that provides access to external services or resources. Drivers abstract away implementation details, making your application code cleaner and more maintainable.
Drivers handle:
- Database connections (Doctrine DBAL/ORM, CouchDB)
- Caching (Memcached, File cache)
- Message queues (RabbitMQ)
- HTTP clients
- Logging
- Configuration management
- You get the idea...
Driver Architecture
Drivers in Kipchak follow a consistent pattern:
- Initialisation - Drivers are initialized once during application bootstrap
- Configuration - Each driver reads its configuration from
config/files - Static Access - Drivers provide static methods for easy access throughout your application
- Container Integration - Drivers register services with the dependency injection container
Drivers essentially inject the dependency in Kipchak's service container so using the $container->set() so it can be retrieved via $container->get(). They use a static class to do this as Kipchak bootstraps. This has multiple advantages:
- You don't have to pass around your container into every class in the API to then get the dependency.
- You can simply call the
Drivername::get()method anywhere and retreive the dependency you need from the container. - This frees you from excessive composition but you still inject instantiate the dependency only once, even if you use it in 100 places.
- It enabled type-hinting / intellisense in your IDE automatically, which you don't get by using the
$container->get()without manually adding a doc bloc for the output.
Initializing Drivers
Drivers are initialized in drivers/drivers.php:
<?php
use Kipchak\Driver\Config\Config;
use Kipchak\Driver\CouchDB\CouchDB;
use Kipchak\Driver\Doctrine\Doctrine;
use Kipchak\Driver\Filecache\Filecache;
use Kipchak\Driver\Http\Http;
use Kipchak\Driver\Logger\Logger;
use Kipchak\Driver\Memcached\Memcached;
use Psr\Container\ContainerInterface;
/**
* @var ContainerInterface $container
*/
// Order matters - some drivers depend on others
Config::initialise($container);
Logger::initialise($container);
Http::initialise($container);
Filecache::initialise($container);
Memcached::initialise($container);
CouchDB::initialise($container);
Doctrine::initialise($container);
Note: Order is important. Config, Logger, and HTTP are initialized first since other drivers may depend on them.
Official Drivers
See the Drivers section of this section on how to use the official drivers.
Using Driver
Drivers are easily accessible in your controllers, models or anywhere else in Kipchak:
<?php
namespace Api\Controllers\v1;
use Kipchak\Core\Components\Controllers;
use Kipchak\Core\Components\Http;
use Kipchak\Driver\Memcached\Memcached;
use Kipchak\Driver\CouchDB\CouchDB;
use Kipchak\Driver\Doctrine\Doctrine;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Contracts\Cache\ItemInterface;
class Users extends Controllers\Base
{
public function list(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
// Use cache
$cache = Memcached::get('cache');
$users = $cache->get('users_list', function (ItemInterface $item) {
$item->expiresAfter(300);
// Fetch from database
$dbal = Doctrine::getDBAL('primary');
return $dbal->createQueryBuilder()
->select('*')
->from('users')
->executeQuery()
->fetchAllAssociative();
});
return Http\Response::json($response, ['users' => $users], 200);
}
public function get(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
$userId = $args['id'];
// Use CouchDB
$db = CouchDB::get('default');
$user = $db->read($userId);
return Http\Response::json($response, json_decode($user, true), 200);
}
}
Driver Patterns
Static Access
All drivers provide static methods for convenience:
$cache = Memcached::get('cache');
$db = CouchDB::get('default');
$logger = Logger::get();
Named Connections
Many drivers support multiple named connections:
// Memcached pools
$cache = Memcached::get('cache');
$sessions = Memcached::get('sessions');
// CouchDB connections
$primary = CouchDB::get('default');
$analytics = CouchDB::get('analytics');
// Doctrine connections
$primary = Doctrine::getDBAL('primary');
$secondary = Doctrine::getDBAL('secondary');
Container Integration
Drivers register themselves with the DI container:
// In a controller
$logger = $this->container->get('logger');
$config = $this->container->get('config');
Best Practices
- Initialize early - Call
::initialise()during application bootstrap - Use named connections - Organize connections by purpose (cache, sessions, etc.)
- Configure via environment variables - Keep sensitive data out of config files
- Cache expensive operations - Use Memcached or File cache for frequently accessed data
- Use ORM for complex queries - DBAL for simple queries, ORM for relationships
- Log appropriately - Use different log levels (debug, info, warning, error)
- Handle failures gracefully - Always check for connection errors and timeouts
- Clean up resources - Close connections when appropriate
Available Drivers
Browse available drivers at https://1x.ax/mamluk/kipchak/drivers
Core Drivers
- Config - Configuration management
- Logger - Monolog logging
- HTTP - HTTP client (Guzzle)
- Filecache - File-based caching
- Memcached - Memcached caching
- CouchDB - CouchDB database
- Doctrine - DBAL and ORM for SQL databases
- RabbitMQ - RabbitMQ Connectivity for Kipchak
Kipchak Framework: Driver Development Guide
A Kipchak Driver is a standalone package that provides a standardized bridge between the framework and external services (like databases, caches, or message brokers). To ensure compatibility, every driver must adhere to the Kipchak\Core\Interfaces\Driver contract.
1. The Driver Interface
Every driver must implement the Kipchak\Core\Interfaces\Driver interface. This standardized structure allows the driver to be initialized during the bootstrap process of the Kipchak kernel.
The interface requires a static initialise method:
Example:
namespace YourNamespace\Driver\Custom;
use Kipchak\Core\Interfaces\Driver as DriverInterface;
use Psr\Container\ContainerInterface;
class Driver implements DriverInterface
{
/**
* @param ContainerInterface $container The DI Container
* @param string|null $configName Optional custom config file name
*/
public static function initialise(ContainerInterface $container, ?string $configName = null): void
{
// 1. Load configuration from /config
// 2. Setup the external service (e.g., Memcached, Doctrine)
// 3. Register the service into the Container
}
}
2. Implementation Example: Memcached Pattern (Static Access)
Based on the kipchak/driver-memcached implementation, drivers often use a static property to hold connection instances. This allows developers to access the driver without always relying on Dependency Injection.
Example Pattern:
namespace Kipchak\Driver\Memcached;
class Driver implements \Kipchak\Core\Interfaces\Driver
{
protected static array $instances = [];
public static function initialise(ContainerInterface $container, ?string $configName = null): void
{
// Default to 'memcached.php' config if no name provided
$config = $container->get('config')->get($configName ?? 'memcached');
foreach ($config['pools'] as $name => $settings) {
// Initialization logic...
self::$instances[$name] = new MemcachedAdapter($client);
}
}
public static function get(string $pool = 'default')
{
return self::$instances[$pool] ?? null;
}
}
3. Implementation Example: Doctrine Pattern (Container Injection)
Based on the kipchak/driver-doctrine implementation, more complex drivers focus on registering services directly into the PSR-11 Container. This allows the Entity Manager to be auto-wired into Controllers.
Example Pattern:
public static function initialise(ContainerInterface $container, ?string $configName = null): void
{
$config = $container->get('config')->get($configName ?? 'doctrine');
// Build the Doctrine Entity Manager using the config...
$entityManager = EntityManager::create($connection, $ormConfig);
// Set the service in the container
$container->set('em', $entityManager);
}
4. Registering the Driver
To activate your driver in the Kipchak starter project, you must register it in the drivers/drivers.php file.
Example Registration:
// drivers/drivers.php
use YourNamespace\Driver\Custom\Driver as CustomDriver;
// The kernel calls this during boot
CustomDriver::initialise($container);
5. Driver Development Best Practices
| Practice | Description |
|---|---|
| Config Independence | Allow $configName to be overridden so a project can have multiple driver instances (e.g., cache_fast.php and cache_slow.php). |
| Environment Aware | Use the Kipchak\Core\env helper within your config files to allow .env overrides. |
| Namespace Hygiene | Keep your driver logic in a clean namespace like Kipchak\Driver\Name. |
| Interface Strictness | Always typehint ContainerInterface to ensure compatibility with the Slim/PHP-DI container. |