Location: PHPKode > scripts > IPC Shared Memory > ipc-shared-memory/ShmOp.php
<?php
// +----------------------------------------------------------------------+
// | PHP extensions shmop en sysvsem based shared memory class for PHP5.  |
// | Copyright (C) 2005 Craig Manley                                      |
// +----------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU Lesser General Public License as       |
// | published by the Free Software Foundation; either version 2.1 of the |
// | License, or (at your option) any later version.                      |
// |                                                                      |
// | This library is distributed in the hope that it will be useful, but  |
// | WITHOUT ANY WARRANTY; without even the implied warranty of           |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU     |
// | Lesser General Public License for more details.                      |
// |                                                                      |
// | You should have received a copy of the GNU Lesser General Public     |
// | License along with this library; if not, write to the Free Software  |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  |
// | USA                                                                  |
// |                                                                      |
// | LGPL license URL: http://opensource.org/licenses/lgpl-license.php    |
// +----------------------------------------------------------------------+
// | Author: Craig Manley                                                 |
// +----------------------------------------------------------------------+
//
// $Id: ShmOp.php,v 1.4 2006/06/19 15:57:41 cmanley Exp $
//



/**
 * @author    Craig Manley
 * @copyright Copyright © 2005, Craig Manley. All rights reserved.
 * @package   IPC_SharedMem
 * @version   $Revision: 1.4 $
 */


/**
 * @ignore Require interface class.
 */
require_once(dirname(__FILE__) . '/../SharedMem.php');


/**
 * PHP extensions shmop en sysvsem based shared memory class.
 * Makes use of the shmop and semaphore functions in PHP. These are not
 * compiled it by default so you've got to make sure they're available
 * if you want to use this class. These extensions aren't available on
 * all platforms.
 *
 * @package  IPC_SharedMem
 * @see      http://www.php.net/manual/en/ref.shmop.php
 * @see      http://www.php.net/manual/en/ref.sem.php
 */
class IPC_SharedMem_ShmOp implements IPC_ISharedMem {

  // Private members
  private $key           = null;
  private $option_perms  = null;
  private $option_remove = false;
  private $sem_id        = null; // only set if in transaction mode.

  /**
   * Class method that throws an exception if the required PHP extensions shmop and sysvsem are not loaded.
   */
  public static function check_required_ext() {
    $shmop   = extension_loaded('shmop');
    $sysvsem = extension_loaded('sysvsem');
    if ($shmop && $sysvsem) {
      return true;
    }
    $missing = array();
    if (!$shmop) {
      array_push($missing, 'shmop');
    }
    if (!$sysvsem) {
      array_push($missing, 'sysvsem');
    }
    if (count($missing) == 1) {
      throw new Exception('The PHP extension ' . $missing[0] . ' required by class ' . __CLASS__ . ' is not loaded or built into PHP.');
    }
    throw new Exception('The PHP extensions ' . implode(' and ', $missing) . ' required by class ' . __CLASS__ . ' are not loaded or built into PHP.');
  }


  /**
   * Constructor.
   *
   * If the shared memory segment with the key passed in the 1st parameter does not exist,
   * then it will be created automatically when needed. The key must be a 32 bit integer
   * or a 4 character string. If it's the latter, then it will be used to generate a
   * 32 bit integer key.
   *
   * The following options can be set in the 2nd parameter:
   * <ul>
   *  <li>perms  - the octal shared memory permissions.</li>
   *  <li>remove - boolean, delete the shared memory when this object is destroyed, default false.</li>
   * </ul>
   *
   * @param mixed $key the access key.
   * @param array $options associative array of options.
   * @return object
   */
  public function __construct($key, $options = null) {
    // Define IPC_CREAT if it is not defined already.
    defined('IPC_CREAT') || define('IPC_CREAT', 0x00001000);

    // Check the key.
    $croak = false;
    if (is_string($key)) {
      if (strlen($key) == 4) {
        //intel endian
        $key = ord(substr($key,0,1)) | (ord(substr($key,1,1))<<8) | (ord(substr($key,2,1))<<16) | (ord(substr($key,3,1)) << 24);
      }
      elseif (preg_match('/^[+-]?\d{1,11}$/', $key)) {
        $key += 0;
      }
      else {
        $croak = true;
      }
    }
    elseif (!is_int($key)) {
      $croak = true;
    }
    if ($croak) {
      throw new Exception("Key is not an integer nor a 4 character string!");
    }
    $this->key = $key;
    // Set options
    if (isset($options)) {
      if (isset($options['perms'])) {
        $this->option_perms = $options['perms'];
      }
      if (isset($options['remove'])) {
        $this->option_remove = $options['remove'];
      }
    }
    if (is_null($this->option_perms)) {
      $this->option_perms = 0666 & ~umask();
    }
  }


  /**
   * Destructor. Removes the shared memory segment if the 'remove' option is true.
   */
  public function __destruct() {
    if ($this->option_remove) {
      $this->transaction_start();
      $shm_id = $this->_open_existing();
      if (isset($shm_id)) {
        shmop_delete($shm_id);
        shmop_close($shm_id);
      }
    }
    $this->transaction_finish();
  }


  /**
   * Opens an existing shared memory segment and returns the handle.
   *
   * @return integer
   */
  protected function _open_existing() {
    $key   = $this->key;
    $perms = $this->option_perms;
    $mode  = 'w';
    $size  = 0;
    //printf("Call _open::shmop_open(%x, '%s', 0%o, %u)\n", $key, $mode, $perms, $size);
    return @shmop_open($key, $mode, $perms, $size);
  }


  /**
   * Opens an existing or creates a new memory segment with the given size.
   * Returns the handle on success, else it throws an exception.
   *
   * @param  integer $size
   * @return integer
   */
  protected function _open($size) {
    // Try to open an existing shm.
    $result = $this->_open_existing();
    if ($result) {
      if (shmop_size($result) == $size) {
        return $result;
      }
      shmop_delete($result);
      shmop_close($result);
    }
    // Create a new shm.
    $key   = $this->key;
    $perms = $this->option_perms;
    $mode = 'c';
    //printf("Call _open::shmop_open(%x, '%s', 0%o, %u)\n", $key, $mode, $perms, $size);
    $result = shmop_open($key, $mode, $perms, $size);
    if (!$result) {
      $this->transaction_finish(); // just in case the caller doesn't do it.
      throw new Exception(sprintf('shmop_open(%x, "%s", 0%o, %u) failed.', $key, $mode, $perms, $size));
    }
    //print "shmop_open() result: \"$result\"\n";
    return $result;
  }


  /**
   * Determines if this object is in transaction mode (i.e. it has a semaphore lock).
   *
   * @return boolean
   */
  public function in_transaction() {
    return isset($this->sem_id) && $this->sem_id;
  }

  /**
   * Opens and acquires a semaphore.
   *
   * @return boolean
   */
  public function transaction_start() {
    if (!$this->in_transaction()) {
      $key   = $this->key;
      $perms = $this->option_perms;
      if (!($this->sem_id = sem_get($key, 1, $perms | IPC_CREAT))) {
        throw new Exception(sprintf('sem_get(%x, 1, 0%o | IPC_CREAT) failed.', $key, $perms));
      }
      if (!sem_acquire($this->sem_id)) {
        throw new Exception(sprintf('sem_acquire(%x) failed.', $this->sem_id));
      }
      return true;
    }
    return false;
  }


  /**
   * Releases the semphore.
   *
   * @return boolean
   */
  public function transaction_finish() {
    if ($this->in_transaction()) {
      $result = sem_release($this->sem_id);
      if ($this->option_remove) {
        sem_remove($this->sem_id);
      }
      $this->sem_id = null;
      return $result;
    }
    return false;
  }


  /**
   * Returns all data from the shared memory segment.
   * If this is called while not in transaction mode, then a
   * transaction is automatically used within this call.
   *
   * @return string
   */
  public function fetch() {
    $atomic = !$this->in_transaction();
    if ($atomic) {
      $this->transaction_start();
    }
    $shm_id = $this->_open_existing();
    $result = '';
    if ($shm_id) {
      $result = shmop_read($shm_id, 0, shmop_size($shm_id));
      shmop_close($shm_id);
    }
    if ($atomic) {
      $this->transaction_finish();
    }
    return $result;
  }


  /**
   * Writes the given string to the shared memory segment.
   * If this is called while not in transaction mode, then an
   * exclusive transaction is automatically used within this call.
   * Returns the number of bytes written.
   *
   * @param string $value
   * @return integer
   */
  public function store($value) {
    $atomic = !$this->in_transaction();
    if ($atomic) {
      $this->transaction_start();
    }
    $shm_id = $this->_open(strlen($value));
    $result = shmop_write($shm_id, $value, 0);
    shmop_close($shm_id);
    if ($atomic) {
      $this->transaction_finish();
    }
    return $result;
  }

}



/**
 * @ignore Check that the shmop and sysvsem extensions are loaded.
 */
IPC_SharedMem_ShmOp::check_required_ext();

?>
Return current item: IPC Shared Memory