Go to the documentation of this file.00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00018 class AggregateCache extends SelectivePeer
00019 {
00020 const LEVEL_ULTRAHIGH = 0xFFFF;
00021 const LEVEL_HIGH = 0xC000;
00022 const LEVEL_NORMAL = 0x8000;
00023 const LEVEL_LOW = 0x4000;
00024 const LEVEL_VERYLOW = 0x0001;
00025
00026 protected $peers = array();
00027 private $levels = array();
00028
00032 public static function create()
00033 {
00034 return new self;
00035 }
00036
00040 public function addPeer(
00041 $label, CachePeer $peer, $level = self::LEVEL_NORMAL
00042 )
00043 {
00044 if (isset($this->peers[$label]))
00045 throw new WrongArgumentException(
00046 'use unique names for your peers'
00047 );
00048
00049 if ($peer->isAlive()) {
00050 $this->peers[$label]['object'] = $peer;
00051 $this->peers[$label]['level'] = $level;
00052 $this->peers[$label]['stat'] = array();
00053 $this->alive = true;
00054 }
00055
00056 return $this;
00057 }
00058
00062 public function dropPeer($label)
00063 {
00064 if (!isset($this->peers[$label]))
00065 throw new MissingElementException(
00066 "there is no peer with '{$label}' label"
00067 );
00068
00069 unset($this->peer[$label]);
00070
00071 return $this;
00072 }
00073
00077 public function setClassLevel($class, $level)
00078 {
00079 $this->levels[$class] = $level;
00080
00081 return $this;
00082 }
00083
00084 public function checkAlive()
00085 {
00086 $this->alive = false;
00087
00088 foreach ($this->peers as $label => $peer)
00089 if ($peer['object']->isAlive())
00090 $this->alive = true;
00091 else
00092 unset($this->peers[$label]);
00093
00094 return $this->alive;
00095 }
00096
00101 public function increment($key, $value)
00102 {
00103 $label = $this->guessLabel($key);
00104
00105 if ($this->peers[$label]['object']->isAlive())
00106 return $this->peers[$label]['object']->increment($key, $value);
00107 else
00108 $this->checkAlive();
00109
00110 return null;
00111 }
00112
00113 public function decrement($key, $value)
00114 {
00115 $label = $this->guessLabel($key);
00116
00117 if ($this->peers[$label]['object']->isAlive())
00118 return $this->peers[$label]['object']->decrement($key, $value);
00119 else
00120 $this->checkAlive();
00121
00122 return null;
00123 }
00124
00125 public function get($key)
00126 {
00127 $label = $this->guessLabel($key);
00128
00129 if ($this->peers[$label]['object']->isAlive())
00130 return $this->peers[$label]['object']->get($key);
00131 else
00132 $this->checkAlive();
00133
00134 return null;
00135 }
00136
00137 public function getList($indexes)
00138 {
00139 $labels = array();
00140 $out = array();
00141
00142 foreach ($indexes as $index)
00143 $labels[$this->guessLabel($index)][] = $index;
00144
00145 foreach ($labels as $label => $indexList)
00146 if ($this->peers[$label]['object']->isAlive()) {
00147 if ($list = $this->peers[$label]['object']->getList($indexList))
00148 $out = array_merge($out, $list);
00149 } else
00150 $this->checkAlive();
00151
00152 return $out;
00153 }
00154
00155 public function delete($key)
00156 {
00157 $label = $this->guessLabel($key);
00158
00159 if (!$this->peers[$label]['object']->isAlive()) {
00160 $this->checkAlive();
00161 return false;
00162 }
00163
00164 return $this->peers[$label]['object']->delete($key);
00165 }
00166
00170 public function clean()
00171 {
00172 foreach ($this->peers as $peer)
00173 $peer['object']->clean();
00174
00175 $this->checkAlive();
00176
00177 return parent::clean();
00178 }
00179
00180 public function getStats()
00181 {
00182 $stats = array();
00183
00184 foreach ($this->peers as $level => $peer)
00185 $stats[$level] = $peer['stat'];
00186
00187 return $stats;
00188 }
00189
00190 public function append($key, $data)
00191 {
00192 $label = $this->guessLabel($key);
00193
00194 if ($this->peers[$label]['object']->isAlive())
00195 return $this->peers[$label]['object']->append($key, $data);
00196 else
00197 $this->checkAlive();
00198
00199 return false;
00200 }
00201
00202 protected function store(
00203 $action, $key, $value, $expires = Cache::EXPIRES_MINIMUM
00204 )
00205 {
00206 $label = $this->guessLabel($key);
00207
00208 if ($this->peers[$label]['object']->isAlive())
00209 return
00210 $this->peers[$label]['object']->$action(
00211 $key,
00212 $value,
00213 $expires
00214 );
00215 else
00216 $this->checkAlive();
00217
00218 return false;
00219 }
00220
00224 protected function guessLabel($key)
00225 {
00226 $class = $this->getClassName();
00227
00228 if (isset($this->levels[$class]))
00229 $classLevel = $this->levels[$class];
00230 else
00231 $classLevel = self::LEVEL_NORMAL;
00232
00233
00234 mt_srand(hexdec(substr(md5($key), 3, 7)));
00235
00236 $zeroDistances = array();
00237 $weights = array();
00238
00239 foreach ($this->peers as $label => $peer) {
00240 $distance = abs($classLevel - $peer['level']);
00241
00242 if (!$distance)
00243 $zeroDistances[] = $label;
00244 else
00245 $weights[$peer['level']] = 1 / pow($distance, 2);
00246 }
00247
00248 if (count($zeroDistances)) {
00249
00250 $selectedLabel =
00251 $zeroDistances[mt_rand(0, count($zeroDistances) - 1)];
00252
00253 } else {
00254
00255
00256 $sum = mt_rand() * array_sum($weights) / mt_getrandmax();
00257 $peerLevel = null;
00258 foreach ($weights as $level => $weight) {
00259 if ($sum <= $weight) {
00260 $peerLevel = $level;
00261 break;
00262 } else
00263 $sum -= $weight;
00264 }
00265
00266 $selectedPeers = array();
00267 foreach ($this->peers as $label => $peer) {
00268 if ($peer['level'] == $peerLevel)
00269 $selectedPeers[] = $label;
00270 }
00271
00272 $selectedLabel = $selectedPeers[mt_rand(0, count($selectedPeers) - 1)];
00273 }
00274
00275 if (isset($this->peers[$selectedLabel]['stat'][$class]))
00276 ++$this->peers[$selectedLabel]['stat'][$class];
00277 else
00278 $this->peers[$selectedLabel]['stat'][$class] = 1;
00279
00280
00281 mt_srand(
00282 (int) (
00283 (int) (microtime(true) << 2)
00284 * (rand(time() / 2, time()) >> 2)
00285 )
00286 );
00287
00288 return $selectedLabel;
00289 }
00290 }
00291 ?>