SharedMemory.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2005-2008 by Konstantin V. Arkhipov                     *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU Lesser General Public License as        *
00007  *   published by the Free Software Foundation; either version 3 of the    *
00008  *   License, or (at your option) any later version.                       *
00009  *                                                                         *
00010  ***************************************************************************/
00011 
00017     final class SharedMemory extends SelectivePeer
00018     {
00019         const INDEX_SEGMENT         = 12345678;
00020         
00021         const DEFAULT_SEGMENT_SIZE  = 4194304; // 128^3 * 2
00022         
00023         private $defaultSize = null;
00024         private $customSized = array();
00025         
00026         private static $attached = array();
00027         
00031         public static function create(
00032             $defaultSize = self::DEFAULT_SEGMENT_SIZE,
00033             $customSized = array()
00034         )
00035         {
00036             return new self($defaultSize, $customSized);
00037         }
00038         
00042         public function __construct(
00043             $defaultSize = self::DEFAULT_SEGMENT_SIZE,
00044             $customSized = array() // 'className' => segmentSizeInBytes
00045         )
00046         {
00047             $this->defaultSize = $defaultSize;
00048             $this->customSized = $customSized;
00049         }
00050         
00051         public function __destruct()
00052         {
00053             foreach (self::$attached as $segment)
00054                 shm_detach($segment);
00055             
00056             // sync classes
00057             $segment = shm_attach(
00058                 self::INDEX_SEGMENT, self::DEFAULT_SEGMENT_SIZE, ONPHP_IPC_PERMS
00059             );
00060             
00061             try {
00062                 $index = shm_get_var($segment, 1);
00063             } catch (BaseException $e) {
00064                 $index = array();
00065             }
00066             
00067             try {
00068                 shm_put_var(
00069                     $segment,
00070                     1,
00071                     array_unique(
00072                         array_merge(
00073                             $index, array_keys(self::$attached)
00074                         )
00075                     )
00076                 );
00077             } catch (BaseException $e) {/*_*/}
00078             
00079             try {
00080                 shm_detach($segment);
00081             } catch (BaseException $e) {/*_*/}
00082         }
00083         
00084         public function increment($key, $value)
00085         {
00086             if (null !== ($current = $this->get($key))) {
00087                 $this->set($key, $current += $value);
00088                 return $current;
00089             }
00090             
00091             return null;
00092         }
00093         
00094         public function decrement($key, $value)
00095         {
00096             if (null !== ($current = $this->get($key))) {
00097                 $this->set($key, $current -= $value);
00098                 return $current;
00099             }
00100             
00101             return null;
00102         }
00103         
00104         public function get($key)
00105         {
00106             $segment = $this->getSegment();
00107             
00108             $key = $this->stringToInt($key);
00109             
00110             try {
00111                 $stored = shm_get_var($segment, $key);
00112                 
00113                 if ($stored['expires'] <= time()) {
00114                     $this->delete($key);
00115                     return null;
00116                 }
00117                 
00118                 return $this->restoreData($stored['value']);
00119                 
00120             } catch (BaseException $e) {
00121                 // not found there
00122                 return null;
00123             }
00124             
00125             Assert::isUnreachable();
00126         }
00127         
00128         public function delete($key)
00129         {
00130             try {
00131                 return shm_remove_var(
00132                     $this->getSegment(), $this->stringToInt($key)
00133                 );
00134             } catch (BaseException $e) {
00135                 return false;
00136             }
00137             
00138             Assert::isUnreachable();
00139         }
00140         
00141         public function isAlive()
00142         {
00143             // any better idea how to detect shm-availability?
00144             return true;
00145         }
00146         
00150         public function clean()
00151         {
00152             $segment = shm_attach(self::INDEX_SEGMENT);
00153             
00154             try {
00155                 $index = shm_get_var($segment, 1);
00156             } catch (BaseException $e) {
00157                 // nothing to clean
00158                 return null;
00159             }
00160             
00161             foreach ($index as $key) {
00162                 try {
00163                     $sem = shm_attach($this->stringToInt($key));
00164                     shm_remove($sem);
00165                 } catch (BaseException $e) {
00166                     // already removed, probably
00167                 }
00168             }
00169             
00170             shm_remove($segment);
00171             
00172             return parent::clean();
00173         }
00174         
00175         public function append($key, $data)
00176         {
00177             $segment = $this->getSegment();
00178             
00179             $key = $this->stringToInt($key);
00180             
00181             try {
00182                 $stored = shm_get_var($segment, $key);
00183                 
00184                 if ($stored['expires'] <= time()) {
00185                     $this->delete($key);
00186                     return false;
00187                 }
00188                 
00189                 return $this->store(
00190                     'ignored',
00191                     $key,
00192                     $stored['value'].$data,
00193                     $stored['expires']
00194                 );
00195             } catch (BaseException $e) {
00196                 // not found there
00197                 return false;
00198             }
00199         }
00200         
00201         protected function store($action, $key, $value, $expires = 0)
00202         {
00203             $segment = $this->getSegment();
00204             
00205             if ($expires < parent::TIME_SWITCH)
00206                 $expires += time();
00207             
00208             try {
00209                 shm_put_var(
00210                     $segment,
00211                     $this->stringToInt($key),
00212                     array(
00213                         'value' => $this->prepareData($value),
00214                         'expires' => $expires
00215                     )
00216                 );
00217                 
00218                 return true;
00219                 
00220             } catch (BaseException $e) {
00221                 // not enough memory
00222                 return false;
00223             }
00224             
00225             Assert::isUnreachable();
00226         }
00227         
00228         private function getSegment()
00229         {
00230             $class = $this->getClassName();
00231             
00232             if (!isset(self::$attached[$class]))
00233                 self::$attached[$class] = shm_attach(
00234                     $this->stringToInt($class),
00235                     isset($this->customSized[$class])
00236                         ? $this->customSized[$class]
00237                         : $this->defaultSize,
00238                     ONPHP_IPC_PERMS
00239                 );
00240             
00241             return self::$attached[$class];
00242         }
00243         
00244         private function stringToInt($string)
00245         {
00246             return hexdec(substr(md5($string), 0, 8));
00247         }
00248     }
00249 ?>