00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00019 final class AggregateCache extends SelectivePeer
00020 {
00021 const LEVEL_ULTRAHIGH = 0xFFFF;
00022 const LEVEL_HIGH = 0xC000;
00023 const LEVEL_NORMAL = 0x8000;
00024 const LEVEL_LOW = 0x4000;
00025 const LEVEL_VERYLOW = 0x0001;
00026
00027 private $peers = array();
00028 private $levels = array();
00029
00033 public static function create()
00034 {
00035 return new self;
00036 }
00037
00041 public function addPeer(
00042 $label, CachePeer $peer, $level = self::LEVEL_NORMAL
00043 )
00044 {
00045 if (isset($this->peers[$label]))
00046 throw new WrongArgumentException(
00047 'use unique names for your peers'
00048 );
00049
00050 if ($peer->isAlive()) {
00051 $this->peers[$label]['object'] = $peer;
00052 $this->peers[$label]['level'] = $level;
00053 $this->peers[$label]['stat'] = array();
00054 $this->alive = true;
00055 }
00056
00057 return $this;
00058 }
00059
00063 public function dropPeer($label)
00064 {
00065 if (!isset($this->peers[$label]))
00066 throw new MissingElementException(
00067 "there is no peer with '{$label}' label"
00068 );
00069
00070 unset($this->peer[$label]);
00071
00072 return $this;
00073 }
00074
00078 public function setClassLevel($class, $level)
00079 {
00080 $this->levels[$class] = $level;
00081
00082 return $this;
00083 }
00084
00085 public function checkAlive()
00086 {
00087 $this->alive = false;
00088
00089 foreach ($this->peers as $label => &$peer)
00090 if ($peer['object']->isAlive())
00091 $this->alive = true;
00092 else
00093 unset($this->peers[$label]);
00094
00095 return $this->alive;
00096 }
00097
00102 public function get($key)
00103 {
00104 $label = $this->guessLabel($key);
00105
00106 if ($this->peers[$label]['object']->isAlive())
00107 return $this->peers[$label]['object']->get($key);
00108 else
00109 $this->checkAlive();
00110
00111 return null;
00112 }
00113
00114 public function delete($key)
00115 {
00116 $label = $this->guessLabel($key);
00117
00118 if (!$this->peers[$label]['object']->isAlive()) {
00119 $this->checkAlive();
00120 return false;
00121 }
00122
00123 return $this->peers[$label]['object']->delete($key);
00124 }
00125
00129 public function clean()
00130 {
00131 foreach ($this->peers as &$peer)
00132 $peer['object']->clean();
00133
00134 $this->checkAlive();
00135
00136 return parent::clean();
00137 }
00138
00139 public function getStats()
00140 {
00141 $stats = array();
00142
00143 foreach ($this->peers as $level => &$peer)
00144 $stats[$level] = $peer['stat'];
00145
00146 return $stats;
00147 }
00148
00149 protected function store(
00150 $action, $key, &$value, $expires = Cache::EXPIRES_MINIMUM
00151 )
00152 {
00153 $label = $this->guessLabel($key);
00154
00155 if ($this->peers[$label]['object']->isAlive())
00156 return
00157 $this->peers[$label]['object']->$action(
00158 $key,
00159 $value,
00160 $expires
00161 );
00162 else
00163 $this->checkAlive();
00164
00165 return false;
00166 }
00167
00171 private function guessLabel($key)
00172 {
00173 $class = $this->getClassName();
00174
00175 if (isset($this->levels[$class]))
00176 $classLevel = $this->levels[$class];
00177 else
00178 $classLevel = self::LEVEL_NORMAL;
00179
00180 mt_srand(hexdec(substr(md5($key), 3, 7)));
00181
00182 $zeroDistances = array();
00183 $weights = array();
00184
00185 foreach ($this->peers as $label => $peer) {
00186 $distance = abs($classLevel - $peer['level']);
00187
00188 if (!$distance)
00189 $zeroDistances[] = $label;
00190 else
00191 $weights[$peer['level']] = 1 / pow($distance, 2);
00192 }
00193
00194 if (count($zeroDistances)) {
00195
00196 $selectedLabel =
00197 $zeroDistances[mt_rand(0, count($zeroDistances) - 1)];
00198
00199 } else {
00200
00201
00202 $sum = mt_rand() * array_sum($weights) / mt_getrandmax();
00203 $peerLevel = null;
00204 foreach ($weights as $level => $weight) {
00205 if ($sum <= $weight) {
00206 $peerLevel = $level;
00207 break;
00208 } else
00209 $sum -= $weight;
00210 }
00211
00212 $selectedPeers = array();
00213 foreach ($this->peers as $label => $peer) {
00214 if ($peer['level'] == $peerLevel)
00215 $selectedPeers[] = $label;
00216 }
00217
00218 $selectedLabel = $selectedPeers[mt_rand(0, count($selectedPeers) - 1)];
00219 }
00220
00221 if (isset($this->peers[$selectedLabel]['stat'][$class]))
00222 ++$this->peers[$selectedLabel]['stat'][$class];
00223 else
00224 $this->peers[$selectedLabel]['stat'][$class] = 1;
00225
00226 return $selectedLabel;
00227 }
00228 }
00229 ?>