Viewing file: shm.php (8.68 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php // +----------------------------------------------------------------------+ // | PEAR :: Cache | // +----------------------------------------------------------------------+ // | Copyright (c) 1997-2003 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.0 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> | // | Sebastian Bergmann <sb@sebastian-bergmann.de> | // +----------------------------------------------------------------------+ // // $Id: shm.php 186977 2005-05-25 10:00:41Z dufuz $
require_once 'Cache/Container.php';
/** * Stores cache data into shared memory. * * Well, this is not a very efficient implementation. Indeed it's much * slower than the file container as far as my tests showed. Files are * cached by most operating systems and it will be hard to write a faster * caching algorithm using PHP. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version $Id: shm.php 186977 2005-05-25 10:00:41Z dufuz $ * @package Cache */ class Cache_Container_shm extends Cache_Container { /** * Key of the semaphore used to sync the SHM access * * @var int */ var $sem_key = null;
/** * Permissions of the semaphore used to sync the SHM access * * @var int */ var $sem_perm = 0644;
/** * Semaphore handler * * @var resource */ var $sem_id = null;
/** * Key of the shared memory block used to store cache data * * @var int */ var $shm_key = null;
/** * Size of the shared memory block used * * Note: the container does only use _one_ shm block no more! * * @var int */ var $shm_size = 131072;
/** * Permissions of the shared memory block * * @var int */ var $shm_perm = 0644;
/** * Shared memory handler * * @var resource */ var $shm_id = null;
/** * Hash of cache entries * * Used by the garbage collection to find old entries. * * @var array */ var $entries = array();
/** * Number of bytes consumed by the cache * * @var int */ var $total_size = 0;
/** * Creates a shared memory container * * @param array shm_key, sem_key, shm_size, sem_perm, shm_perm */ function Cache_Container_shm($options = '') { if (is_array($options)) { $this->setOptions($options, array_merge($this->allowed_options, array('shm_key', 'sem_key', 'shm_size', 'sem_perm', 'shm_perm' ) ) ); } // Cache::Container high- and lowwater defaults should be overridden if // not already done by the user if (!isset($options['highwater'])) { $this->highwater = round(0.75 * 131072); } if (!isset($options['lowwater'])) { $this->lowwater = round(0.5 * 131072); } if (!isset($options['shm_size'])) { $this->shm_size = 131072; } //get SHM and Semaphore handles if (!($this->shm_id = shmop_open($this->shm_key, 'c', $this->shm_perm, $this->shm_size))) { new Cache_Error("Can't open SHM segment '{$this->shm_key}', size '{$this->shm_size}'.", __FILE__, __LINE__ ); } if (!($this->sem_id = sem_get($this->sem_key, 1, $this->sem_perm))) { new Cache_Error("Can't get semaphore '{$this->sem_key}' using perms '{$this->sem_perm}'.", __FILE__, __LINE__ ); } } // end constructor
function fetch($id, $group) { sem_acquire($this->sem_id);
$cachedata = shmop_read($this->shm_id, 0, $this->shm_size);
sem_release($this->sem_id);
$cachedata = $this->decode($cachedata);
if (!isset($cachedata[$group][$id])) { return array(null, null, null); } else { $cachedata = $cachedata[$group][$id]; } return array($cachedata['expire'], $cachedata['cachedata'], $cachedata['userdata'] ); } // end func fetch
function save($id, $data, $expire, $group, $userdata) { $this->flushPreload($id, $group);
sem_acquire($this->sem_id);
$cachedata = $this->decode(shmop_read($this->shm_id, 0, $this->shm_size)); $cachedata[$group][$id] = array('expire' => $this->getExpiresAbsolute($expire), 'cachedata' => $data, 'userdata' => $userdata, 'changed' => time() );
if (strlen($newdata = $this->encode($cachedata)) > $this->shm_size) { $cachedata = $this->garbageCollection(time(), $cachedata); } shmop_write($this->shm_id, $newdata, 0);
sem_release($this->sem_id);
return true; } // end func save
function remove($id, $group) { $this->flushPreload($id, $group);
sem_acquire($this->sem_id);
$cachedata = $this->decode(shmop_read($this->shm_id, 0, $this->shm_size)); unset($cachedata[$group][$id]); shmop_write($this->shm_id, $this->encode($cachedata), 0);
sem_release($this->sem_id); } // end func remove
function flush($group = '') { $this->flushPreload();
sem_acquire($this->sem_id);
shmop_write($this->shm_id, $this->encode(array()), 0);
sem_release($this->sem_id); } // end func flush
function idExists($id, $group) { sem_acquire($this->sem_id);
$cachedata = shm_read($this->shm_id, 0, $this->shm_size);
sem_release($this->sem_id);
$cachedata = $this->decode($cachedata);
return isset($cachedata[$group][$id]); } // end func isExists
function garbageCollection($maxlifetime, $cachedata = array()) { if ($lock = empty($cachedata)) { sem_acquire($this->sem_id); $cachedata = $this->decode(shmop_read($this->shm_id, 0, $this->shm_size)); }
$this->doGarbageCollection($maxlifetime, $cachedata); if ($this->total_size > $this->highwater) { krsort($this->entries); reset($this->entries);
while ($this->total_size > $this->lowwater && list($size, $entries) = each($this->entries)) { reset($entries);
while (list($k, $entry) = each($entries)) { unset($cachedata[$entry['group']][$entry['id']]); $this->total_size -= $size; } } }
if ($lock) { sem_release($this->sem_id); } $this->entries = array(); $this->total_size = 0;
return $cachedata; } // end func garbageCollection
function doGarbageCollection($maxlifetime, &$cachedata) { $changed = time() - $maxlifetime; $removed = 0;
reset($cachedata);
while (list($group, $groupdata) = each($cachedata)) { reset($groupdata);
while (list($id, $data) = each($groupdata)) { if ($data['expire'] < time() || $data['changed'] < $changed) { unset($cachedata[$group][$id]); } }
// ugly but simple to implement :/ $size = strlen($this->encode($data)); $this->entries[$size][] = array( 'group' => $group, 'id' => $id );
$this->total_size += $size; }
return $removed; } // end func doGarbageCollection } // end class Cache_Container_shm ?>
|