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:

  1. Initialisation - Drivers are initialized once during application bootstrap
  2. Configuration - Each driver reads its configuration from config/ files
  3. Static Access - Drivers provide static methods for easy access throughout your application
  4. 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

  1. Initialize early - Call ::initialise() during application bootstrap
  2. Use named connections - Organize connections by purpose (cache, sessions, etc.)
  3. Configure via environment variables - Keep sensitive data out of config files
  4. Cache expensive operations - Use Memcached or File cache for frequently accessed data
  5. Use ORM for complex queries - DBAL for simple queries, ORM for relationships
  6. Log appropriately - Use different log levels (debug, info, warning, error)
  7. Handle failures gracefully - Always check for connection errors and timeouts
  8. 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

PracticeDescription
Config IndependenceAllow $configName to be overridden so a project can have multiple driver instances (e.g., cache_fast.php and cache_slow.php).
Environment AwareUse the Kipchak\Core\env helper within your config files to allow .env overrides.
Namespace HygieneKeep your driver logic in a clean namespace like Kipchak\Driver\Name.
Interface StrictnessAlways typehint ContainerInterface to ensure compatibility with the Slim/PHP-DI container.
Previous
Configuration