# 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 ?>