gjerokrsteski/pimf-framework

View on GitHub
core/Pimf/Cache/Storages/Dba.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Pimf
 *
 * @copyright Copyright (c)  Gjero Krsteski (http://krsteski.de)
 * @license   http://opensource.org/licenses/MIT MIT License
 */

namespace Pimf\Cache\Storages;

/**
 * This class provides the functionality required to store
 * and retrieve PHP strings, integers or arrays.
 *
 * It uses the database (dbm-style) abstraction layer for persistence.
 * Even instances of SimpleXMLElement can be stored. You don't have
 * to matter about the size of the cache-file. It depends on the free
 * space of your disk.
 *
 * @package Cache_Storages
 * @author  Gjero Krsteski <gjero@krsteski.de>
 */
class Dba extends Storage
{
    /**
     * @var resource
     */
    protected $dba;

    /**
     * @var string
     */
    protected $handler;

    /**
     * @var string
     */
    protected $file;

    /**
     * @param string  $file    the cache-file.
     *
     * @param string  $handler the dba handler.
     *
     * You have to install one of this handlers before use.
     *
     * cdb      = Tiny Constant Database - for reading.
     * cdb_make = Tiny Constant Database - for writing.
     * db4      = Oracle Berkeley DB 4   - for reading and writing.
     * qdbm     = Quick Database Manager - for reading and writing.
     * gdbm     = GNU Database Manager   - for reading and writing.
     * flatfile = default dba extension  - for reading and writing.
     *
     * Use flatfile-handler only when you cannot install one,
     * of the libraries required by the other handlers,
     * and when you cannot use bundled cdb handler.
     *
     * @param string  $mode    For read/write access, database creation if it doesn't currently exist.
     *
     * @param boolean $persistently
     *
     * @throws \RuntimeException If no DBA extension or handler installed.
     */
    public function __construct($file, $handler = 'flatfile', $mode = 'c', $persistently = true)
    {
        if (false === extension_loaded('dba')) {
            throw new \RuntimeException('The DBA extension is required for this wrapper, but the extension is not loaded');
        }

        if (false === in_array($handler, dba_handlers(false))) {
            throw new \RuntimeException('The ' . $handler . ' handler is required for the DBA extension, but the handler is not installed');
        }

        $this->dba = (true === $persistently) ? dba_popen($file, $mode, $handler) : dba_open($file, $mode, $handler);

        $this->file = $file;
        $this->handler = $handler;
    }

    /**
     * Closes an open dba resource
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->dba) {
            dba_close($this->dba);
            $this->dba = null;
        }
    }

    /**
     * @param string $key
     * @param mixed  $value
     * @param int    $minutes
     *
     * @return bool
     */
    public function put($key, $value, $minutes)
    {
        if ($minutes <= 0) {
            return;
        }

        $value = $this->expiration($minutes) . serialize($value);

        if (true === $this->has($key)) {
            return dba_replace($key, $value, $this->dba);
        }

        return dba_insert($key, $value, $this->dba);
    }

    /**
     * @param string $key
     * @param null   $default
     *
     * @return bool|mixed|null
     */
    public function get($key, $default = null)
    {
        $res = $this->retrieve($key);

        if (false === $res) {
            $this->forget($key);

            return false;
        }

        return $res;
    }

    /**
     * @param string $key
     *
     * @return bool|mixed
     */
    protected function retrieve($key)
    {
        $value = dba_fetch($key, $this->dba);

        if (false === $value) {
            return null;
        }

        // compare the timestamp to the current time when we read the value.
        if (time() >= substr($value, 0, 10)) {
            $this->forget($key);
            return null;
        }

        return unserialize(substr($value, 10));
    }

    /**
     * @param string $key
     *
     * @return boolean
     */
    public function forget($key)
    {
        if (false === is_resource($this->dba)) {
            return false;
        }

        return dba_delete($key, $this->dba);
    }

    /**
     * @param string $key
     *
     * @return boolean
     */
    public function has($key)
    {
        return dba_exists($key, $this->dba);
    }

    /**
     * Write an item to the cache for five years.
     *
     * @param $key
     * @param $value
     *
     * @return boolean
     */
    public function forever($key, $value)
    {
        return $this->put($key, $value, 2628000);
    }

    /**
     * Cleans and optimizes the cache from all expired entries.
     *
     * @return bool
     */
    public function clean()
    {
        $dba = $this->dba;
        $key = dba_firstkey($dba);

        while ($key !== false && $key !== null) {
            $this->retrieve($key);
            $key = dba_nextkey($dba);
        }

        return dba_optimize($dba);
    }

    /**
     * Flush the whole storage.
     *
     * @return bool
     */
    public function flush()
    {
        if (file_exists($this->file)) {

            // We close the dba file before deleting
            // and reopen on next use.
            $this->__destruct();

            unlink($this->file);

            clearstatcache();

            return true;
        }

        return false;
    }

    /**
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }
}