php-yaoi/php-yaoi

View on GitHub
src/Storage/Driver/DatabaseProxy.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php

namespace Yaoi\Storage\Driver;

use Yaoi\App;
use Yaoi\BaseClass;
use Yaoi\Database;
use Yaoi\Date\TimeMachine;
use Yaoi\Migration\ClosureMigration;
use Yaoi\Sql\Symbol;
use Yaoi\Storage\Contract\Driver;
use Yaoi\Storage\Contract\Expire;
use Yaoi\Storage\Exception;
use Yaoi\Storage\Settings;

class DatabaseProxy extends BaseClass implements Driver, Expire,
    \Yaoi\Migration\Needed
{
    private $dsn;
    /**
     * @var Database
     */
    private $db;

    /**
     * @var TimeMachine
     */
    private $time;

    /**
     * @var Symbol
     */
    private $table;
    /**
     * @var Symbol
     */
    private $keyField;
    /**
     * @var Symbol
     */
    private $valueField;
    /**
     * @var Symbol
     */
    private $expireField;

    public function __construct(Settings $dsn = null)
    {
        $this->dsn = $dsn ? $dsn : new Settings();
        if (empty($dsn->proxyClient)) {
            throw new Exception('proxyClient required in dsn', Exception::PROXY_REQUIRED);
        }
        $this->table = new Symbol($dsn->path
            ? $dsn->path : 'key_value_storage');
        $this->keyField = new Symbol('k');
        $this->valueField = new Symbol('v');
        $this->expireField = new Symbol('e');


        $this->db = Database::getInstance($this->dsn->proxyClient);
        $this->time = App::time($this->dsn->dateSource); // TODO use getInstance after Date_Source refactoring
    }

    public function set($key, $value, $ttl)
    {
        if (is_array($value) || is_object($value)) {
            throw new Exception('Complex data types not supported by this storage, consider serialization',
                Exception::SCALAR_REQUIRED);
        }

        if ($ttl && $ttl < 30 * 86400) {
            $ttl = $this->time->now() + $ttl;
        }

        if ($this->keyExists($key)) {
            /** @noinspection PhpMethodParametersCountMismatchInspection */
            $this->db
                ->update($this->table)
                ->set("? = ?", $this->valueField, $value)
                ->set('? = ?', $this->expireField, $ttl)
                ->where('? = ?', $this->keyField, $key)
                ->query();
        } else {
            $this->db->insert($this->table)->valuesRow(
                array(
                    $this->keyField->name => $key,
                    $this->valueField->name => $value,
                    $this->expireField->name => $ttl,
                ))
                ->query();
        }
    }

    public function get($key)
    {
        /** @noinspection PhpMethodParametersCountMismatchInspection */
        $row = $this->db
            ->select($this->table)
            ->select('?, ?', $this->valueField, $this->expireField)
            ->where('? = ?', $this->keyField, $key)
            ->query()
            ->fetchRow();

        if (!$row) {
            return null;
        } else {
            $expire = $row[$this->expireField->name];
            if ($expire && $expire < $this->time->now()) {
                $this->delete($key);
                return null;
            }
            return $row[$this->valueField->name];
        }
    }

    public function keyExists($key)
    {
        /** @noinspection PhpMethodParametersCountMismatchInspection */
        $row = $this->db
            ->select($this->table)
            ->select('?', $this->expireField)
            ->where('? = ?', $this->keyField, $key)
            ->query()
            ->fetchRow();

        if (!$row) {
            return false;
        } else {
            $expireUt = $row[$this->expireField->name];
            if ($expireUt && $this->time->now() > $expireUt) {
                $this->delete($key);
                return false;
            }
            return true;
        }
    }

    public function delete($key)
    {
        /** @noin2spection PhpMethodParametersCountMismatchInspection */
        $this->db
            ->delete($this->table)
            ->where('? = ?', $this->keyField, $key)
            ->query()
            ->execute();
    }

    public function deleteAll()
    {
        $this->db
            ->delete($this->table)
            ->query()
            ->execute();
    }

    /**
     * @return ClosureMigration
     */
    public function getMigration()
    {
        $migrationId = 'storage_db_wrapper' . '_' . get_class($this->db->getDriver()) . $this->table->name;
        $table = $this->table;
        $keyField = $this->keyField;
        $valueField = $this->valueField;
        $expireField = $this->expireField;
        $db = $this->db;
        return new ClosureMigration($migrationId, function() use ($table, $keyField, $valueField, $expireField, $db) {
            //if ($this->db->getDriver() instanceof Database_Server_Mysql) {
            $db->query("CREATE TABLE IF NOT EXISTS :table (
:key VARCHAR(255) NOT NULL DEFAULT '',
:value TEXT NOT NULL,
:expire INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (:key)
)",
                array(
                    'table' => $table,
                    'key' => $keyField,
                    'value' => $valueField,
                    'expire' => $expireField,
                ));
            //}
            return true;

        }, function() use ($table, $db) {
            $db->query("DROP TABLE ?", $table);
            return true;
        });
    }
}