模拟PDOStatement :: execute

时间:2014-12-04 14:51:22

标签: php caching pdo memcached

我正在尝试使用Memcache中的函数扩展我的PDO类。特别是,我创建了一个函数,我调用cache_execute,它允许我首先检查结果是否存在于缓存中,然后返回缓存中的信息,或者来自数据库的信息,如果信息缓存中尚不存在(如果信息尚未存在,则将信息放入缓存中)

以下是我当前代码的示例:

namespace trf;
class StatementWithCaching extends \PDOStatement {
    public $db;
    public $cache;
    protected function __construct(&$db) {
        $this->db =& $db;
    }

    function cache_execute( $array = NULL , $cache = FALSE , $cacheTime = 1800 ) {
        if( is_a( $cache , 'Memcache' ) ) {
            $query = $this->queryString;
            foreach ($array as $key => $value) {
                $query = str_replace(':' . $key , "'" . addslashes( $value ) . "'", $query );
            }
            try {
                $memResults = $this->mconn->get( md5( $this->queryString ) );
            }
            catch ( \Exception $e ) {
                $memResults = FALSE;
            }

        } else {

        }
    }
}

我的问题是 - 如何放置从缓存中检索到的信息,并将其存储在PDOStatement::fetch()PDOStatement::fetchAll()等可以检索的位置。

我的目标是能够运行StatementWithCaching::execute()StatementWithCaching::cache_execute()并仍以相同的方式检索结果(使用StatementWithCaching::fetch()StatementWithCaching::fetchAll())。

2 个答案:

答案 0 :(得分:1)

我强烈建议您不要将缓存访问逻辑与数据库访问逻辑混合使用。换句话说,我不会尝试扩展特定于DB的类以便写入缓存。毕竟,为什么在不需要的时候尝试将语句,结果集等概念引入简单的缓存层。

我会寻找更高级别的抽象,也许是一个类,你可以传递一个有效的数据库连接和一个有效的连接到你的缓存层(在你的情况下是Memcache),并让类编排逻辑来读取/写入缓存和DB。

作为高级示例(显然这省略了错误/异常处理等):

class WriteTroughCache {
    protected $pdo;
    protected $cache;
    protected $cache_ttl = 1000;
    // not shown variouos other options that might be needed for cache or DB

    public function __construct(PDO $pdo, Memcache $cache, $options = array()) {
        $this->pdo = $pdo;
        $this->cache = $cache;
        if (!empty($options)) {
            // not shown load options from array into option properties including cache_ttl
        }    
    }

    public get($key) {
        $result = $this-cache->get($key);
        if ($result) {
            return $result;
        }
        // not in cache, get from DB
        $value = $this->db_get($key);
        if (false === $value) {
            // key did not exist in DB
            return false;
        } else {
            // key found in DB
            $this->cache->set($key, $value, 0, $this->cache_ttl);
            return $value;
        }
    }

    public function set($key, $value) {
        // see if key exists
        // if so, update cache first
        $value = $this->get($key);
        if($value) {
            $this->cache->set($key, $value, 0 , $this->cache_ttl);
        }           
        return $this->db_set($key, $value);
    }

    public function db_get($key) {
        // not shown perform DB query using PDO object
        // return value at key if found or false if not found
    }

    public function db_set($key, $value) {
        // not shown perform DB query using PDO object
        // return true or false based on success of insert
    }
}

答案 1 :(得分:0)

我能够通过扩展PDOStatement::fetch()PDOStatement::fetchAll()函数来解决问题。

namespace trf;
class StatementWithCaching extends \PDOStatement {
    protected $db;
    protected $cache;
    protected $cacheReturn;
    protected $cacheQuery;
    protected $queryCacheTime;
    protected function __construct(&$db) {
        $this->db =& $db;
    }

    function cache_execute( $array = NULL , $cache = FALSE , $cacheTime = 1800 ) {
        if( is_a( $cache , 'Memcache' ) ) {
            $this->cache = $cache;
            $this->queryCacheTime = $cacheTime;
            $this->cacheQuery = $this->queryString;
            if( $array !== NULL ) {
                foreach ($array as $key => $value) {
                    $this->cacheQuery = str_replace(':' . $key , "'" . addslashes( $value ) . "'", $this->cacheQuery );
                }
            }
            $this->debugData( 'Trying to get data from cache for query: ' . $this->cacheQuery . ' (' . md5( $this->cacheQuery ) . ')' );
            $this->cacheQuery = md5( $this->cacheQuery );
            try {
                $this->cacheReturn = $this->cache->get( $this->cacheQuery );
                $this->debugData( 'Reporting return: ' . var_export( $this->cacheReturn , TRUE ) );
            }
            catch ( \Exception $e ) {
                $this->cacheReturn = FALSE;
                $this->debugData( $e->getMessage() );
            }
            if( is_null( $this->cacheReturn ) || $this->cacheReturn == FALSE || is_null( $this->cacheQuery ) || !is_a( $this->cache , 'Memcache' ) ) {
                if ($array === null) {
                    parent::execute();
                } else {
                    parent::execute($array);
                }
            }   
        } else {
            if ($array === null) {
                parent::execute();
            } else {
                parent::execute($array);
            }
        }
    }

    function fetch( $fetchStyle = \PDO::FETCH_BOTH, $cursor_orientation = \PDO::FETCH_ORI_NEXT , $cursor_offset = 0 ) {
        if( is_null( $this->cacheReturn ) || $this->cacheReturn == FALSE || is_null( $this->cacheQuery ) || !is_a( $this->cache , 'Memcache' ) ) {
            $fullResults = parent::fetchAll();
            if( is_a( $this->cache , 'Memcache' ) ) {
                $this->debugData( 'Inserting data into cache:' . print_r( $fullResults , TRUE ) );
                $this->cache->set($this->cacheQuery , $fullResults , MEMCACHE_COMPRESSED , $cacheTime );
            }
            return parent::fetch( $fetchStyle , $cursor_orientation = \PDO::FETCH_ORI_NEXT , $cursor_offset = 0 );
        }
        else {
            $this->debugData( 'Returning Cached Results' );
            switch ($fetchStyle) {
                case \PDO::FETCH_BOTH:
                    return $this->cacheReturn[$cursor_offset];
                    break;

                case \PDO::FETCH_ASSOC:
                    $data = $this->cacheReturn[$cursor_offset];
                    foreach ($data as $key => $value) {
                        if( is_numeric( $key ) ) {
                            unset( $data[$key] );
                        }
                    }
                    return $data;
                    break;

                case \PDO::FETCH_LAZY:
                    $data = $this->cacheReturn[$cursor_offset];
                    $return = new \stdClass();
                    foreach ($data as $key => $value) {
                        if( !is_numeric( $key ) ) {
                            $return->$key = $value;
                        }
                    }
                    return $return;
                    break;

                case \PDO::FETCH_OBJ:
                    $data = $this->cacheReturn[$cursor_offset];
                    $return = new \stdClass();
                    foreach ($data as $key => $value) {
                        if( !is_numeric( $key ) ) {
                            $return->$key = $value;
                        }
                    }
                    return $return;
                    break;

                default:
                    return $this->cacheReturn[$cursor_offset];
                    break;
            }
        }
    }

    function fetchAll() {
        if( is_null( $this->cacheReturn ) || $this->cacheReturn == FALSE || is_null( $this->cacheQuery ) || !is_a( $this->cache , 'Memcache' ) ) {
            $fullResults = parent::fetchAll();
            if( is_a( $this->cache , 'Memcache' ) ) {
                $this->debugData( 'Inserting data into cache: ' . print_r( $fullResults , TRUE ) );
                $this->cache->set($this->cacheQuery , $fullResults , MEMCACHE_COMPRESSED , $this->queryCacheTime );
            }
            return $fullResults;
        } else {
            $this->debugData( 'Returning Cached Results' );
            return $this->cacheReturn;
        }
    }

    private function debugData( $data ) {
        if( isset( $_GET['debug'] ) && $_GET['debug'] = DEBUGVAL ) {
            print('<pre>');
            print_r( $data );
            print('</pre>');
        }
    }
}

虽然解决方案仍然无法处理可以为PDOStatement::fetch()PDOStatement::fetchAll()设置的所有标志,但可以进一步扩展该标志。

到目前为止,我已经进行了一些非常好的测试。