<?php
/**
* PHP Memcache Queue class
* @author lkk/lianq.net
* @version 0.3
* @ Modification Instructions:
* 1. Abandoned the previous AB rotation idea, using a similar array of constructs, rewrite this class.
* 2. Queue default advanced first out, but added reverse read function.
* 3. Thank netizens Foxhunter for their valuable opinions.
* @example:
* $obj = new Memcachequeue (' Duilie ');
* $obj->add (' 1asdf ');
* $obj->getqueuelength ();
* $obj->read (10);
* $obj->get (8);
*/
Class memcachequeue{
public static $client; Memcache Client Connections
Public $access; Whether the queue can be updated
Private $expire; Expiration time, seconds, 1~2592000, i.e. within 30 days
Private $sleepTime; Wait for unlock time, microsecond
Private $queueName; Queue name, unique value
Private $retryNum; Number of retries, = 10 * Theoretical concurrency
Public $currentHead; Current team first value
Public $currentTail; Current team Tail value
Const MAXNUM = 20000; Maximum number of queues, recommended Upper 10K
Const Head_key = ' _lkkqueuehead_ '; Queue Head Kye
Const Tail_key = ' _lkkqueuetail_ '; Queue Tail Key
Const Valu_key = ' _lkkqueuevalu_ '; Queue Value Key
Const Lock_key = ' _lkkqueuelock_ '; Queue Lock key
/**
* Constructor
* @param string $queueName queue name
* @param int $expire Expiration Time
* @param array $config memcache configuration
*
* @return <type>
*/
Public function __construct ($queueName = ', $expire =0, $config = ') {
if (empty ($config)) {
Self:: $client = Memcache_pconnect (' 127.0.0.1 ', 11211);
}elseif (Is_array ($config)) {//array (' host ' => ' 127.0.0.1 ', ' Port ' => ' 11211 ')
Self:: $client = Memcache_pconnect ($config [' Host '], $config [' Port ']);
}elseif (is_string ($config)) {//"127.0.0.1:11211"
$tmp = Explode (': ', $config);
$conf [' host '] = isset ($tmp [0])? $tmp [0]: ' 127.0.0.1 ';
$conf [' port '] = Isset ($tmp [1])? $tmp [1]: ' 11211 ';
Self:: $client = Memcache_pconnect ($conf [' Host '], $conf [' Port ']);
}
if (!self:: $client) return false;
Ignore_user_abort (TRUE);///When customer disconnects, allow continued execution
Set_time_limit (0)//Cancel script execution delay limit
$this->access = false;
$this->sleeptime = 1000;
$expire = Empty ($expire)? 3600:intval ($expire) +1;
$this->expire = $expire;
$this->queuename = $queueName;
$this->retrynum = 1000;
$this->head_key = $this->queuename. Self::head_key;
$this->tail_key = $this->queuename. Self::tail_key;
$this->lock_key = $this->queuename. Self::lock_key;
$this->_initsetheadntail ();
}
/**
* Initialize the queue to set the End-to-end value
*/
Private Function _initsetheadntail () {
The number at the beginning of the current queue
$this->currenthead = Memcache_get (self:: $client, $this->head_key);
if ($this->currenthead = false) $this->currenthead = 0;
numeric value at the end of the current queue
$this->currenttail = Memcache_get (self:: $client, $this->tail_key);
if ($this->currenttail = false) $this->currenttail = 0;
}
/**
* When the element is taken out, change the number of the head of the queue
* @param int $step Step value
*/
Private Function _changehead ($step =1) {
$this->currenthead + + $step;
Memcache_set (self:: $client, $this->head_key, $this->currenthead,false, $this->expire);
}
/**
* Change the value at the end of the queue when adding elements
* @param int $step Step value
* @param bool $reverse is reverse
* @return NULL
*/
Private Function _changetail ($step =1, $reverse =false) {
if (! $reverse) {
$this->currenttail + + $step;
}else{
$this->currenttail-= $step;
}
Memcache_set (self:: $client, $this->tail_key, $this->currenttail,false, $this->expire);
}
/**
* Whether the queue is empty
* @return BOOL
*/
Private Function _isempty () {
Return (BOOL) ($this->currenthead = = $this->currenttail);
}
/**
* Whether the queue is full
* @return BOOL
*/
Private Function _isfull () {
$len = $this->currenttail-$this->currenthead;
Return (bool) ($len = = Self::maxnum);
}
/**
* Queue Lock
*/
Private Function _getlock () {
if ($this->access = = False) {
while (!memcache_add (self:: $client, $this->lock_key, 1, False, $this->expire)) {
Usleep ($this->sleeptime);
@ $i + +;
if ($i > $this->retrynum) {//try to wait n times
return false;
Break
}
}
$this->_initsetheadntail ();
return $this->access = true;
}
return $this->access;
}
/**
* Queue unlock
*/
Private Function _unlock () {
Memcache_delete (self:: $client, $this->lock_key, 0);
$this->access = false;
}
/**
* Get the length of the current queue
* This length is theoretical length, some elements are lost due to expiration, true length <= that length
* @return int
*/
Public Function Getqueuelength () {
$this->_initsetheadntail ();
Return Intval ($this->currenttail-$this->currenthead);
}
/**
* Add Queue data
* @param void $data The data to be added
* @return BOOL
*/
Public function Add ($data) {
if (! $this->_getlock ()) return false;
if ($this->_isfull ()) {
$this->_unlock ();
return false;
}
$value _key = $this->queuename. Self::valu_key. Strval ($this->currenttail + 1);
$result = Memcache_set (self:: $client, $value _key, $data, memcache_compressed, $this->expire);
if ($result) {
$this->_changetail ();
}
$this->_unlock ();
return $result;
}
/**
* Read queue data
* @param int $length The length to be read (negative read used in reverse)
* @return Array
*/
Public function read ($length =0) {
if (!is_numeric ($length)) return false;
$this->_initsetheadntail ();
if ($this->_isempty ()) {
return false;
}
if (empty ($length)) $length = self::maxnum;//Default All
$KEYARR = Array ();
if ($length >0) {//forward read (from Queue head to queue end)
$tmpMin = $this->currenthead;
$tmpMax = $tmpMin + $length;
for ($i = $tmpMin; $i <= $tmpMax; $i + +) {
$KEYARR [] = $this->queuename. Self::valu_key. $i;
}
}else{//backward read (from queue end to queue head)
$tmpMax = $this->currenttail;
$tmpMin = $tmpMax + $length;
for ($i = $tmpMax; $i > $tmpMin; $i-) {
$KEYARR [] = $this->queuename. Self::valu_key. $i;
}
}
$result = @memcache_get (self:: $client, $KEYARR);
return $result;
}
/**
* Remove Queue data
* @param the length of the int $length to be taken out (negative reading is used)
* @return Array
*/
Public function Get ($length =0) {
if (!is_numeric ($length)) return false;
if (! $this->_getlock ()) return false;
if ($this->_isempty ()) {
$this->_unlock ();
return false;
}
if (empty ($length)) $length = self::maxnum;//Default All
$length = Intval ($length);
$KEYARR = Array ();
if ($length >0) {//forward read (from Queue head to queue end)
$tmpMin = $this->currenthead;
$tmpMax = $tmpMin + $length;
for ($i = $tmpMin; $i <= $tmpMax; $i + +) {
$KEYARR [] = $this->queuename. Self::valu_key. $i;
}
$this->_changehead ($length);
}else{//backward read (from queue end to queue head)
$tmpMax = $this->currenttail;
$tmpMin = $tmpMax + $length;
for ($i = $tmpMax; $i > $tmpMin; $i-) {
$KEYARR [] = $this->queuename. Self::valu_key. $i;
}
$this->_changetail (ABS ($length), true);
}
$result = @memcache_get (self:: $client, $KEYARR);
foreach ($keyArr as $v) {//Removed after removal
@memcache_delete (self:: $client, $v, 0);
}
$this->_unlock ();
return $result;
}
/**
* Empty Queue
*/
Public Function Clear () {
if (! $this->_getlock ()) return false;
if ($this->_isempty ()) {
$this->_unlock ();
return false;
}
$tmpMin = $this->currenthead--;
$tmpMax = $this->currenttail++;
for ($i = $tmpMin; $i <= $tmpMax; $i + +) {
$tmpKey = $this->queuename. Self::valu_key. $i;
@memcache_delete (self:: $client, $tmpKey, 0);
}
$this->currenttail = $this->currenthead = 0;
Memcache_set (self:: $client, $this->head_key, $this->currenthead,false, $this->expire);
Memcache_set (self:: $client, $this->tail_key, $this->currenttail,false, $this->expire);
$this->_unlock ();
}
/*
* Clear all Memcache cache data
*/
Public Function Memflush () {
Memcache_flush (self:: $client);
}
}//end class