PHPMixBill v5.0 - First Upload

This commit is contained in:
Ibnu Maksum
2017-03-11 02:51:06 +07:00
commit 979475b312
767 changed files with 239450 additions and 0 deletions

17
system/autoload/Admin.php Normal file
View File

@ -0,0 +1,17 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class Admin{
public static function _info(){
$id = $_SESSION['aid'];
$d = ORM::for_table('tbl_users')->find_one($id);
return $d;
}
}

16
system/autoload/App.php Normal file
View File

@ -0,0 +1,16 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class App{
public static function _run(){
return true;
}
}

View File

@ -0,0 +1,243 @@
<?php
namespace PEAR2;
if (!class_exists('\PEAR2\Autoload', false)) {
class Autoload
{
/**
* Whether the autoload class has been spl_autoload_register-ed
*
* @var bool
*/
protected static $registered = false;
/**
* Array of PEAR2 autoload paths registered
*
* @var array
*/
protected static $paths = array();
/**
* Array of classname-to-file mapping
*
* @var array
*/
protected static $map = array();
/**
* Array of class maps loaded
*
* @var array
*/
protected static $maps = array();
/**
* Last classmap specified
*
* @var array
*/
protected static $mapfile = null;
/**
* Array of classes loaded automatically not in the map
*
* @var array
*/
protected static $unmapped = array();
/**
* Initialize the PEAR2 autoloader
*
* @param string $path Directory path to register
*
* @return void
*/
static function initialize($path, $mapfile = null)
{
self::register();
self::addPath($path);
self::addMap($mapfile);
}
/**
* Register the PEAR2 autoload class with spl_autoload_register
*
* @return void
*/
protected static function register()
{
if (!self::$registered) {
// set up __autoload
$autoload = spl_autoload_functions();
spl_autoload_register('PEAR2\Autoload::load');
if (function_exists('__autoload') && ($autoload === false)) {
// __autoload() was being used, but now would be ignored, add
// it to the autoload stack
spl_autoload_register('__autoload');
}
}
self::$registered = true;
}
/**
* Add a path
*
* @param string $path The directory to add to the set of PEAR2 paths
*
* @return void
*/
protected static function addPath($path)
{
if (!in_array($path, self::$paths)) {
self::$paths[] = $path;
}
}
/**
* Add a classname-to-file map
*
* @param string $mapfile The filename of the classmap
*
* @return void
*/
protected static function addMap($mapfile)
{
if (! in_array($mapfile, self::$maps)) {
// keep track of specific map file loaded in this
// instance so we can update it if necessary
self::$mapfile = $mapfile;
if (file_exists($mapfile)) {
$map = include $mapfile;
if (is_array($map)) {
// mapfile contains a valid map, so we'll keep it
self::$maps[] = $mapfile;
self::$map = array_merge(self::$map, $map);
}
}
}
}
/**
* Check if the class is already defined in a classmap
*
* @param string $class The class to look for
*
* @return bool
*/
protected static function isMapped($class)
{
if (isset(self::$map[$class])) {
return true;
}
if (isset(self::$mapfile) && ! isset(self::$map[$class])) {
self::$unmapped[] = $class;
return false;
}
return false;
}
/**
* Load a PEAR2 class
*
* @param string $class The class to load
*
* @return bool
*/
static function load($class)
{
// need to check if there's a current map file specified ALSO.
// this could be the first time writing it.
$mapped = self::isMapped($class);
if ($mapped) {
require self::$map[$class];
if (!self::loadSuccessful($class)) {
// record this failure & keep going, we may still find it
self::$unmapped[] = $class;
} else {
return true;
}
}
$file = str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $class) . '.php';
foreach (self::$paths as $path) {
if (file_exists($path . DIRECTORY_SEPARATOR . $file)) {
require $path . DIRECTORY_SEPARATOR . $file;
if (!self::loadSuccessful($class)) {
throw new \Exception('Class ' . $class . ' was not present in ' .
$path . DIRECTORY_SEPARATOR . $file .
'") [PEAR2_Autoload-0.2.4]');
}
if (in_array($class, self::$unmapped)) {
self::updateMap($class, $path . DIRECTORY_SEPARATOR . $file);
}
return true;
}
}
$e = new \Exception('Class ' . $class . ' could not be loaded from ' .
$file . ', file does not exist (registered paths="' .
implode(PATH_SEPARATOR, self::$paths) .
'") [PEAR2_Autoload-0.2.4]');
$trace = $e->getTrace();
if (isset($trace[2]) && isset($trace[2]['function']) &&
in_array($trace[2]['function'], array('class_exists', 'interface_exists'))) {
return false;
}
if (isset($trace[1]) && isset($trace[1]['function']) &&
in_array($trace[1]['function'], array('class_exists', 'interface_exists'))) {
return false;
}
throw $e;
}
/**
* Check if the requested class was loaded from the specified path
*
* @return bool
*/
protected static function loadSuccessful($class)
{
if (!class_exists($class, false) && !interface_exists($class, false)) {
return false;
}
return true;
}
/**
* If possible, update the classmap file with newly-discovered
* mapping.
*
* @param string $class Class name discovered
*
* @param string $origin File where class was found
*
*/
protected static function updateMap($class, $origin)
{
if (is_writable(self::$mapfile) || is_writable(dirname(self::$mapfile))) {
self::$map[$class] = $origin;
file_put_contents(self::$mapfile,
'<'."?php\n"
. "// PEAR2\Autoload auto-generated classmap\n"
. "return " . var_export(self::$map, true) . ';',
LOCK_EX
);
}
}
/**
* return the array of paths PEAR2 autoload has registered
*
* @return array
*/
static function getPaths()
{
return self::$paths;
}
}
}
Autoload::initialize(dirname(__DIR__));

View File

@ -0,0 +1,371 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache;
/**
* Used as a catch-all for adapter initialization.
*/
use Exception as E;
/**
* Implements this class.
*/
use IteratorAggregate;
/**
* Used on failures by this class.
*/
use PEAR2\Cache\SHM\InvalidArgumentException;
/**
* Main class for this package.
*
* Automatically chooses an adapter based on the available extensions.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
abstract class SHM implements IteratorAggregate
{
/**
* @var array An array of adapter names that meet their requirements.
*/
private static $_adapters = array();
/**
* Creates a new shared memory storage.
*
* Estabilishes a separate persistent storage. Adapter is automatically
* chosen based on the available extensions.
*
* @param string $persistentId The ID for the storage.
*
* @return static|SHM A new instance of an SHM adapter (child of this
* class).
*/
final public static function factory($persistentId)
{
foreach (self::$_adapters as $adapter) {
try {
return new $adapter($persistentId);
} catch (E $e) {
//In case of a runtime error, try to fallback to other adapters.
}
}
throw new InvalidArgumentException(
'No appropriate adapter available',
1
);
}
/**
* Checks if the adapter meets its requirements.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function isMeetingRequirements()
{
return true;
}
/**
* Registers an adapter.
*
* Registers an SHM adapter, allowing you to call it with {@link factory()}.
*
* @param string $adapter FQCN of adapter. A valid adapter is one that
* extends this class. The class will be autoloaded if not already
* present.
* @param bool $prepend Whether to prepend this adapter into the list of
* possible adapters, instead of appending to it.
*
* @return bool TRUE on success, FALSE on failure.
*/
final public static function registerAdapter($adapter, $prepend = false)
{
if (class_exists($adapter, true)
&& is_subclass_of($adapter, '\\' . __CLASS__)
&& $adapter::isMeetingRequirements()
) {
if ($prepend) {
self::$_adapters = array_merge(
array($adapter),
self::$_adapters
);
} else {
self::$_adapters[] = $adapter;
}
return true;
}
return false;
}
/**
* Adds a value to the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, or fails if it does.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function __invoke($key, $value, $ttl = 0)
{
return $this->add($key, $value, $ttl);
}
/**
* Gets a value from the shared memory storage.
*
* This is a magic method, thanks to which any property you attempt to get
* the value of will be fetched from the adapter, treating the property name
* as the key of the value to get.
*
* @param string $key Name of key to get.
*
* @return mixed The current value of the specified key.
*/
public function __get($key)
{
return $this->get($key);
}
/**
* Sets a value in the shared memory storage.
*
* This is a magic method, thanks to which any property you attempt to set
* the value of will be set by the adapter, treating the property name as
* the key of the value to set. The value is set without a TTL.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function __set($key, $value)
{
return $this->set($key, $value);
}
/**
* Checks if a specified key is in the storage.
*
* This is a magic method, thanks to which any property you call isset() on
* will be checked by the adapter, treating the property name as the key
* of the value to check.
*
* @param string $key Name of key to check.
*
* @return bool TRUE if the key is in the storage, FALSE otherwise.
*/
public function __isset($key)
{
return $this->exists($key);
}
/**
* Deletes a value from the shared memory storage.
*
* This is a magic method, thanks to which any property you attempt to unset
* the value of will be unset by the adapter, treating the property name as
* the key of the value to delete.
*
* @param string $key Name of key to delete.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function __unset($key)
{
return $this->delete($key);
}
/**
* Creates a new shared memory storage.
*
* Estabilishes a separate persistent storage.
*
* @param string $persistentId The ID for the storage. The storage will be
* reused if it exists, or created if it doesn't exist. Data and locks
* are namespaced by this ID.
*/
abstract public function __construct($persistentId);
/**
* Obtains a named lock.
*
* @param string $key Name of the key to obtain. Note that $key may
* repeat for each distinct $persistentId.
* @param double $timeout If the lock can't be immediatly obtained, the
* script will block for at most the specified amount of seconds.
* Setting this to 0 makes lock obtaining non blocking, and setting it
* to NULL makes it block without a time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function lock($key, $timeout = null);
/**
* Releases a named lock.
*
* @param string $key Name of the key to release. Note that $key may
* repeat for each distinct $persistentId.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function unlock($key);
/**
* Checks if a specified key is in the storage.
*
* @param string $key Name of key to check.
*
* @return bool TRUE if the key is in the storage, FALSE otherwise.
*/
abstract public function exists($key);
/**
* Adds a value to the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, or fails if it does.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function add($key, $value, $ttl = 0);
/**
* Sets a value in the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function set($key, $value, $ttl = 0);
/**
* Gets a value from the shared memory storage.
*
* Gets the current value, or throws an exception if it's not stored.
*
* @param string $key Name of key to get the value of.
*
* @return mixed The current value of the specified key.
*/
abstract public function get($key);
/**
* Deletes a value from the shared memory storage.
*
* @param string $key Name of key to delete.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function delete($key);
/**
* Increases a value from the shared memory storage.
*
* Increases a value from the shared memory storage. Unlike a plain
* set($key, get($key)+$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to increase.
* @param int $step Value to increase the key by.
*
* @return int The new value.
*/
abstract public function inc($key, $step = 1);
/**
* Decreases a value from the shared memory storage.
*
* Decreases a value from the shared memory storage. Unlike a plain
* set($key, get($key)-$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to decrease.
* @param int $step Value to decrease the key by.
*
* @return int The new value.
*/
abstract public function dec($key, $step = 1);
/**
* Sets a new value if a key has a certain value.
*
* Sets a new value if a key has a certain value. This function only works
* when $old and $new are longs.
*
* @param string $key Key of the value to compare and set.
* @param int $old The value to compare the key against.
* @param int $new The value to set the key to.
*
* @return bool TRUE on success, FALSE on failure.
*/
abstract public function cas($key, $old, $new);
/**
* Clears the persistent storage.
*
* Clears the persistent storage, i.e. removes all keys. Locks are left
* intact.
*
* @return void
*/
abstract public function clear();
/**
* Retrieve an external iterator
*
* Returns an external iterator.
*
* @param string|null $filter A PCRE regular expression.
* Only matching keys will be iterated over.
* Setting this to NULL matches all keys of this instance.
* @param bool $keysOnly Whether to return only the keys,
* or return both the keys and values.
*
* @return \Traversable An array with all matching keys as array keys,
* and values as array values. If $keysOnly is TRUE, the array keys are
* numeric, and the array values are key names.
*/
abstract public function getIterator($filter = null, $keysOnly = false);
}
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Placebo');
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Wincache');
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\APC');

View File

@ -0,0 +1,406 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache\SHM\Adapter;
/**
* Throws exceptions from this namespace, and extends from this class.
*/
use PEAR2\Cache\SHM;
/**
* {@link APC::getIterator()} returns this object.
*/
use ArrayObject;
/**
* Shared memory adapter for the APC extension.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
class APC extends SHM
{
/**
* @var string ID of the current storage.
*/
protected $persistentId;
/**
* List of persistent IDs.
*
* A list of persistent IDs within the current request (as keys) with an int
* (as a value) specifying the number of instances in the current request.
* Used as an attempt to ensure implicit lock releases even on errors in the
* critical sections, since APC doesn't have an actual locking function.
* @var array
*/
protected static $requestInstances = array();
/**
* @var array Array of lock names (as values) for each persistent ID (as
* key) obtained during the current request.
*/
protected static $locksBackup = array();
/**
* Creates a new shared memory storage.
*
* Estabilishes a separate persistent storage.
*
* @param string $persistentId The ID for the storage. The storage will be
* reused if it exists, or created if it doesn't exist. Data and locks
* are namespaced by this ID.
*/
public function __construct($persistentId)
{
$this->persistentId = __CLASS__ . ' ' . $persistentId;
if (isset(static::$requestInstances[$this->persistentId])) {
static::$requestInstances[$this->persistentId]++;
} else {
static::$requestInstances[$this->persistentId] = 1;
static::$locksBackup[$this->persistentId] = array();
}
register_shutdown_function(
get_called_class() . '::releaseLocks',
$this->persistentId,
true
);
}
/**
* Checks if the adapter meets its requirements.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function isMeetingRequirements()
{
return extension_loaded('apc')
&& version_compare(phpversion('apc'), '3.0.13', '>=')
&& ini_get('apc.enabled')
&& ('cli' !== PHP_SAPI || ini_get('apc.enable_cli'));
}
/**
* Releases all locks in a storage.
*
* This function is not meant to be used directly. It is implicitly called
* by the the destructor and as a shutdown function when the request ends.
* One of these calls ends up releasing any unreleased locks obtained
* during the request. A lock is also implicitly released as soon as there
* are no objects left in the current request using the same persistent ID.
*
* @param string $internalPersistentId The internal persistent ID, the locks
* of which are being released.
* @param bool $isAtShutdown Whether the function was executed at
* shutdown.
*
* @return void
* @internal
*/
public static function releaseLocks($internalPersistentId, $isAtShutdown)
{
$hasInstances = 0 !== static::$requestInstances[$internalPersistentId];
if ($isAtShutdown === $hasInstances) {
foreach (static::$locksBackup[$internalPersistentId] as $key) {
apc_delete($internalPersistentId . 'l ' . $key);
}
}
}
/**
* Releases any locks obtained by this instance as soon as there are no more
* references to the object's persistent ID.
*/
public function __destruct()
{
static::$requestInstances[$this->persistentId]--;
static::releaseLocks($this->persistentId, false);
}
/**
* Obtains a named lock.
*
* @param string $key Name of the key to obtain. Note that $key may
* repeat for each distinct $persistentId.
* @param double $timeout If the lock can't be immediatly obtained, the
* script will block for at most the specified amount of seconds.
* Setting this to 0 makes lock obtaining non blocking, and setting it
* to NULL makes it block without a time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function lock($key, $timeout = null)
{
$lock = $this->persistentId . 'l ' . $key;
$hasTimeout = $timeout !== null;
$start = microtime(true);
while (!apc_add($lock, 1)) {
if ($hasTimeout && (microtime(true) - $start) > $timeout) {
return false;
}
}
static::$locksBackup[$this->persistentId] = $key;
return true;
}
/**
* Releases a named lock.
*
* @param string $key Name of the key to release. Note that $key may
* repeat for each distinct $persistentId.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function unlock($key)
{
$lock = $this->persistentId . 'l ' . $key;
$success = apc_delete($lock);
if ($success) {
unset(static::$locksBackup[$this->persistentId][array_search(
$key,
static::$locksBackup[$this->persistentId],
true
)]);
return true;
}
return false;
}
/**
* Checks if a specified key is in the storage.
*
* @param string $key Name of key to check.
*
* @return bool TRUE if the key is in the storage, FALSE otherwise.
*/
public function exists($key)
{
return apc_exists($this->persistentId . 'd ' . $key);
}
/**
* Adds a value to the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, or fails if it does.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function add($key, $value, $ttl = 0)
{
return apc_add($this->persistentId . 'd ' . $key, $value, $ttl);
}
/**
* Sets a value in the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function set($key, $value, $ttl = 0)
{
return apc_store($this->persistentId . 'd ' . $key, $value, $ttl);
}
/**
* Gets a value from the shared memory storage.
*
* Gets the current value, or throws an exception if it's not stored.
*
* @param string $key Name of key to get the value of.
*
* @return mixed The current value of the specified key.
*/
public function get($key)
{
$fullKey = $this->persistentId . 'd ' . $key;
if (apc_exists($fullKey)) {
$value = apc_fetch($fullKey, $success);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to fetch key. ' .
'Key has either just now expired or (if no TTL was set) ' .
'is possibly in a race condition with another request.',
100
);
}
return $value;
}
throw new SHM\InvalidArgumentException('No such key in cache', 101);
}
/**
* Deletes a value from the shared memory storage.
*
* @param string $key Name of key to delete.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function delete($key)
{
return apc_delete($this->persistentId . 'd ' . $key);
}
/**
* Increases a value from the shared memory storage.
*
* Increases a value from the shared memory storage. Unlike a plain
* set($key, get($key)+$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to increase.
* @param int $step Value to increase the key by.
*
* @return int The new value.
*/
public function inc($key, $step = 1)
{
$newValue = apc_inc(
$this->persistentId . 'd ' . $key,
(int) $step,
$success
);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to increase the value. Are you sure the value is int?',
102
);
}
return $newValue;
}
/**
* Decreases a value from the shared memory storage.
*
* Decreases a value from the shared memory storage. Unlike a plain
* set($key, get($key)-$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to decrease.
* @param int $step Value to decrease the key by.
*
* @return int The new value.
*/
public function dec($key, $step = 1)
{
$newValue = apc_dec(
$this->persistentId . 'd ' . $key,
(int) $step,
$success
);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to decrease the value. Are you sure the value is int?',
103
);
}
return $newValue;
}
/**
* Sets a new value if a key has a certain value.
*
* Sets a new value if a key has a certain value. This function only works
* when $old and $new are longs.
*
* @param string $key Key of the value to compare and set.
* @param int $old The value to compare the key against.
* @param int $new The value to set the key to.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function cas($key, $old, $new)
{
return apc_cas($this->persistentId . 'd ' . $key, $old, $new);
}
/**
* Clears the persistent storage.
*
* Clears the persistent storage, i.e. removes all keys. Locks are left
* intact.
*
* @return void
*/
public function clear()
{
foreach (new APCIterator(
'user',
'/^' . preg_quote($this->persistentId, '/') . 'd /',
APC_ITER_KEY,
100,
APC_LIST_ACTIVE
) as $key) {
apc_delete($key);
}
}
/**
* Retrieve an external iterator
*
* Returns an external iterator.
*
* @param string|null $filter A PCRE regular expression.
* Only matching keys will be iterated over.
* Setting this to NULL matches all keys of this instance.
* @param bool $keysOnly Whether to return only the keys,
* or return both the keys and values.
*
* @return ArrayObject An array with all matching keys as array keys,
* and values as array values. If $keysOnly is TRUE, the array keys are
* numeric, and the array values are key names.
*/
public function getIterator($filter = null, $keysOnly = false)
{
$result = array();
foreach (new APCIterator(
'user',
'/^' . preg_quote($this->persistentId, '/') . 'd /',
APC_ITER_KEY,
100,
APC_LIST_ACTIVE
) as $key) {
$localKey = strstr($key, $this->persistentId . 'd ');
if (null === $filter || preg_match($filter, $localKey)) {
if ($keysOnly) {
$result[] = $localKey;
} else {
$result[$localKey] = apc_fetch($key);
}
}
}
return new ArrayObject($result);
}
}

View File

@ -0,0 +1,358 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache\SHM\Adapter;
/**
* Throws exceptions from this namespace, and extends from this class.
*/
use PEAR2\Cache\SHM;
/**
* {@link Placebo::getIterator()} returns this object.
*/
use ArrayObject;
/**
* This adapter is not truly persistent. It is intended to emulate persistency
* in non persistent environments, so that upper level applications can use a
* single code path for persistent and non persistent code.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
class Placebo extends SHM
{
/**
* @var string ID of the current storage.
*/
protected $persistentId;
/**
* List of persistent IDs.
*
* A list of persistent IDs within the current request (as keys) with an int
* (as a value) specifying the number of instances in the current request.
* Used as an attempt to ensure implicit lock releases on destruction.
* @var array
*/
protected static $requestInstances = array();
/**
* @var array Array of lock names (as values) for each persistent ID (as
* key) obtained during the current request.
*/
protected static $locksBackup = array();
/**
* The data storage.
*
* Each persistent ID is a key, and the value is an array.
* Each such array has data keys as its keys, and an array as a value.
* Each such array has as its elements the value, the timeout and the time
* the data was set.
* @var array
*/
protected static $data = array();
/**
* Creates a new shared memory storage.
*
* Estabilishes a separate persistent storage.
*
* @param string $persistentId The ID for the storage. The storage will be
* reused if it exists, or created if it doesn't exist. Data and locks
* are namespaced by this ID.
*/
public function __construct($persistentId)
{
if (isset(static::$requestInstances[$persistentId])) {
++static::$requestInstances[$persistentId];
} else {
static::$requestInstances[$persistentId] = 1;
static::$locksBackup[$persistentId] = array();
static::$data[$persistentId] = array();
}
$this->persistentId = $persistentId;
}
/**
* Releases any unreleased locks.
*/
public function __destruct()
{
if (0 === --static::$requestInstances[$this->persistentId]) {
static::$locksBackup[$this->persistentId] = array();
}
}
/**
* Checks if the adapter meets its requirements.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function isMeetingRequirements()
{
return 'cli' === PHP_SAPI;
}
/**
* Pretends to obtain a lock.
*
* @param string $key Ignored.
* @param double $timeout Ignored.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function lock($key, $timeout = null)
{
$key = (string) $key;
if (in_array($key, static::$locksBackup[$this->persistentId], true)) {
return false;
}
static::$locksBackup[$this->persistentId][] = $key;
return true;
}
/**
* Pretends to release a lock.
*
* @param string $key Ignored
*
* @return bool TRUE on success, FALSE on failure.
*/
public function unlock($key)
{
$key = (string) $key;
if (!in_array($key, static::$locksBackup[$this->persistentId], true)) {
return false;
}
unset(static::$locksBackup[$this->persistentId][array_search(
$key,
static::$locksBackup[$this->persistentId],
true
)]);
return true;
}
/**
* Checks if a specified key is in the storage.
*
* @param string $key Name of key to check.
*
* @return bool TRUE if the key is in the storage, FALSE otherwise.
*/
public function exists($key)
{
return array_key_exists($key, static::$data[$this->persistentId]);
}
/**
* Adds a value to the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, or fails if it does.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Because "true" adapters purge the cache at the next
* request, this setting is ignored.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function add($key, $value, $ttl = 0)
{
if ($this->exists($key)) {
return false;
}
return $this->set($key, $value, $ttl);
}
/**
* Sets a value in the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Because "true" adapters purge the cache at the next
* request, this setting is ignored.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function set($key, $value, $ttl = 0)
{
static::$data[$this->persistentId][$key] = $value;
return true;
}
/**
* Gets a value from the shared memory storage.
*
* Gets the current value, or throws an exception if it's not stored.
*
* @param string $key Name of key to get the value of.
*
* @return mixed The current value of the specified key.
*/
public function get($key)
{
if ($this->exists($key)) {
return static::$data[$this->persistentId][$key];
}
throw new SHM\InvalidArgumentException(
'Unable to fetch key. No such key.',
200
);
}
/**
* Deletes a value from the shared memory storage.
*
* @param string $key Name of key to delete.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function delete($key)
{
if ($this->exists($key)) {
unset(static::$data[$this->persistentId][$key]);
return true;
}
return false;
}
/**
* Increases a value from the shared memory storage.
*
* Increases a value from the shared memory storage. Unlike a plain
* set($key, get($key)+$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to increase.
* @param int $step Value to increase the key by.
*
* @return int The new value.
*/
public function inc($key, $step = 1)
{
if (!$this->exists($key) || !is_int($value = $this->get($key))
|| !$this->set($key, $value + (int) $step)
) {
throw new SHM\InvalidArgumentException(
'Unable to increase the value. Are you sure the value is int?',
201
);
}
return $this->get($key);
}
/**
* Decreases a value from the shared memory storage.
*
* Decreases a value from the shared memory storage. Unlike a plain
* set($key, get($key)-$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to decrease.
* @param int $step Value to decrease the key by.
*
* @return int The new value.
*/
public function dec($key, $step = 1)
{
if (!$this->exists($key) || !is_int($value = $this->get($key))
|| !$this->set($key, $value - (int) $step)
) {
throw new SHM\InvalidArgumentException(
'Unable to increase the value. Are you sure the value is int?',
202
);
}
return $this->get($key);
}
/**
* Sets a new value if a key has a certain value.
*
* Sets a new value if a key has a certain value. This function only works
* when $old and $new are longs.
*
* @param string $key Key of the value to compare and set.
* @param int $old The value to compare the key against.
* @param int $new The value to set the key to.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function cas($key, $old, $new)
{
return $this->exists($key) && ($this->get($key) === $old)
&& is_int($new) && $this->set($key, $new);
}
/**
* Clears the persistent storage.
*
* Clears the persistent storage, i.e. removes all keys. Locks are left
* intact.
*
* @return void
*/
public function clear()
{
static::$data[$this->persistentId] = array();
}
/**
* Retrieve an external iterator
*
* Returns an external iterator.
*
* @param string|null $filter A PCRE regular expression.
* Only matching keys will be iterated over.
* Setting this to NULL matches all keys of this instance.
* @param bool $keysOnly Whether to return only the keys,
* or return both the keys and values.
*
* @return ArrayObject An array with all matching keys as array keys,
* and values as array values. If $keysOnly is TRUE, the array keys are
* numeric, and the array values are key names.
*/
public function getIterator($filter = null, $keysOnly = false)
{
if (null === $filter) {
return new ArrayObject(
$keysOnly
? array_keys(static::$data[$this->persistentId])
: static::$data[$this->persistentId]
);
}
$result = array();
foreach (static::$data[$this->persistentId] as $key => $value) {
if (preg_match($filter, $key)) {
$result[$key] = $value;
}
}
return new ArrayObject($keysOnly ? array_keys($result) : $result);
}
}

View File

@ -0,0 +1,383 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache\SHM\Adapter;
/**
* Throws exceptions from this namespace, and extends from this class.
*/
use PEAR2\Cache\SHM;
/**
* {@link Wincache::getIterator()} returns this object.
*/
use ArrayObject;
/**
* Shared memory adapter for the WinCache extension.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
class Wincache extends SHM
{
/**
* @var string ID of the current storage.
*/
protected $persistentId;
/**
* List of persistent IDs.
*
* A list of persistent IDs within the current request (as keys) with an int
* (as a value) specifying the number of instances in the current request.
* Used as an attempt to ensure implicit lock releases on destruction.
* @var array
*/
protected static $requestInstances = array();
/**
* @var array Array of lock names obtained during the current request.
*/
protected static $locksBackup = array();
/**
* Creates a new shared memory storage.
*
* Estabilishes a separate persistent storage.
*
* @param string $persistentId The ID for the storage. The storage will be
* reused if it exists, or created if it doesn't exist. Data and locks
* are namespaced by this ID.
*/
public function __construct($persistentId)
{
$this->persistentId
= static::encodeLockName(__CLASS__ . ' ' . $persistentId) . ' ';
if (isset(static::$requestInstances[$this->persistentId])) {
static::$requestInstances[$this->persistentId]++;
} else {
static::$requestInstances[$this->persistentId] = 1;
static::$locksBackup[$this->persistentId] = array();
}
}
/**
* Encodes a lock name
*
* Encodes a lock name, so that it can be properly obtained. The scheme used
* is a subset of URL encoding, with only the "%" and "\" characters being
* escaped. The encoding itself is necessary, since lock names can't contain
* the "\" character.
*
* @param string $name The lock name to encode.
*
* @return string The encoded name.
* @link http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx
*/
protected static function encodeLockName($name)
{
return str_replace(array('%', '\\'), array('%25', '%5C'), $name);
}
/**
* Checks if the adapter meets its requirements.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function isMeetingRequirements()
{
return extension_loaded('wincache')
&& version_compare(phpversion('wincache'), '1.1.0', '>=')
&& ini_get('wincache.ucenabled')
&& ('cli' !== PHP_SAPI || ini_get('wincache.enablecli'));
}
/**
* Releases any locks obtained by this instance as soon as there are no more
* references to the object's persistent ID.
*/
public function __destruct()
{
if (0 === --static::$requestInstances[$this->persistentId]) {
foreach (static::$locksBackup[$this->persistentId] as $key) {
wincache_unlock(
$this->persistentId . static::encodeLockName($key)
);
}
}
}
/**
* Obtains a named lock.
*
* @param string $key Name of the key to obtain. Note that $key may
* repeat for each distinct $persistentId.
* @param double $timeout Ignored with WinCache. Script will always block if
* the lock can't be immediatly obtained.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function lock($key, $timeout = null)
{
$result = wincache_lock(
$this->persistentId . static::encodeLockName($key)
);
if ($result) {
static::$locksBackup[$this->persistentId] = $key;
}
return $result;
}
/**
* Releases a named lock.
*
* @param string $key Name of the key to release. Note that $key may
* repeat for each distinct $persistentId.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function unlock($key)
{
$result = wincache_unlock(
$this->persistentId . static::encodeLockName($key)
);
if ($result) {
unset(static::$locksBackup[$this->persistentId][array_search(
$key,
static::$locksBackup[$this->persistentId],
true
)]);
}
return $result;
}
/**
* Checks if a specified key is in the storage.
*
* @param string $key Name of key to check.
*
* @return bool TRUE if the key is in the storage, FALSE otherwise.
*/
public function exists($key)
{
return wincache_ucache_exists($this->persistentId . $key);
}
/**
* Adds a value to the shared memory storage.
*
* Sets a value to the storage if it doesn't exist, or fails if it does.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function add($key, $value, $ttl = 0)
{
return wincache_ucache_add($this->persistentId . $key, $value, $ttl);
}
/**
* Sets a value in the shared memory storage.
*
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
*
* @param string $key Name of key to associate the value with.
* @param mixed $value Value for the specified key.
* @param int $ttl Seconds to store the value. If set to 0 indicates no
* time limit.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function set($key, $value, $ttl = 0)
{
return wincache_ucache_set($this->persistentId . $key, $value, $ttl);
}
/**
* Gets a value from the shared memory storage.
*
* Gets the current value, or throws an exception if it's not stored.
*
* @param string $key Name of key to get the value of.
*
* @return mixed The current value of the specified key.
*/
public function get($key)
{
$value = wincache_ucache_get($this->persistentId . $key, $success);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to fetch key. No such key, or key has expired.',
300
);
}
return $value;
}
/**
* Deletes a value from the shared memory storage.
*
* @param string $key Name of key to delete.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function delete($key)
{
return wincache_ucache_delete($this->persistentId . $key);
}
/**
* Increases a value from the shared memory storage.
*
* Increases a value from the shared memory storage. Unlike a plain
* set($key, get($key)+$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to increase.
* @param int $step Value to increase the key by.
*
* @return int The new value.
*/
public function inc($key, $step = 1)
{
$newValue = wincache_ucache_inc(
$this->persistentId . $key,
(int) $step,
$success
);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to increase the value. Are you sure the value is int?',
301
);
}
return $newValue;
}
/**
* Decreases a value from the shared memory storage.
*
* Decreases a value from the shared memory storage. Unlike a plain
* set($key, get($key)-$step) combination, this function also implicitly
* performs locking.
*
* @param string $key Name of key to decrease.
* @param int $step Value to decrease the key by.
*
* @return int The new value.
*/
public function dec($key, $step = 1)
{
$newValue = wincache_ucache_dec(
$this->persistentId . $key,
(int) $step,
$success
);
if (!$success) {
throw new SHM\InvalidArgumentException(
'Unable to decrease the value. Are you sure the value is int?',
302
);
}
return $newValue;
}
/**
* Sets a new value if a key has a certain value.
*
* Sets a new value if a key has a certain value. This function only works
* when $old and $new are longs.
*
* @param string $key Key of the value to compare and set.
* @param int $old The value to compare the key against.
* @param int $new The value to set the key to.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function cas($key, $old, $new)
{
return wincache_ucache_cas($this->persistentId . $key, $old, $new);
}
/**
* Clears the persistent storage.
*
* Clears the persistent storage, i.e. removes all keys. Locks are left
* intact.
*
* @return void
*/
public function clear()
{
$info = wincache_ucache_info();
foreach ($info['ucache_entries'] as $entry) {
if (!$entry['is_session']
&& 0 === strpos($entry['key_name'], $this->persistentId)
) {
wincache_ucache_delete($entry['key_name']);
}
}
}
/**
* Retrieve an external iterator
*
* Returns an external iterator.
*
* @param string|null $filter A PCRE regular expression.
* Only matching keys will be iterated over.
* Setting this to NULL matches all keys of this instance.
* @param bool $keysOnly Whether to return only the keys,
* or return both the keys and values.
*
* @return ArrayObject An array with all matching keys as array keys,
* and values as array values. If $keysOnly is TRUE, the array keys are
* numeric, and the array values are key names.
*/
public function getIterator($filter = null, $keysOnly = false)
{
$info = wincache_ucache_info();
$result = array();
foreach ($info['ucache_entries'] as $entry) {
if (!$entry['is_session']
&& 0 === strpos($entry['key_name'], $this->persistentId)
) {
$localKey = strstr($entry['key_name'], $this->persistentId);
if (null === $filter || preg_match($filter, $localKey)) {
if ($keysOnly) {
$result[] = $localKey;
} else {
$result[$localKey] = apc_fetch($localKey);
}
}
}
}
return new ArrayObject($result);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache\SHM;
/**
* Generic exception class of this package.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
interface Exception
{
}

View File

@ -0,0 +1,35 @@
<?php
/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 0.1.3
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Cache\SHM;
/**
* Exception thrown when there's something wrong with an argument.
*
* @category Caching
* @package PEAR2_Cache_SHM
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Cache_SHM
*/
class InvalidArgumentException extends \InvalidArgumentException
implements Exception
{
}

View File

@ -0,0 +1,369 @@
<?php
/**
* Main class for Console_Color
*
* PHP version 5.3
*
* @category Console
* @package Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @author Ivo Nascimento <ivo@o8o.com.br>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear.php.net/package/Console_Color
*/
namespace PEAR2\Console;
use PEAR2\Console\Color\Backgrounds;
use PEAR2\Console\Color\Flags;
use PEAR2\Console\Color\Fonts;
use PEAR2\Console\Color\Styles;
use PEAR2\Console\Color\UnexpectedValueException;
use ReflectionClass;
/**
* Main class for Console_Color.
*
* @category Console
* @package Console_Color
* @author Ivo Nascimento <ivo@o8o.com.br>
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
class Color
{
/**
* @var array List of valid font colors.
* Filled by {@link fillValidators()}.
*/
protected static $validFonts = array();
/**
* @var array List of valid background colors.
* Filled by {@link fillValidators()}.
*/
protected static $validBackgorunds = array();
/**
* @var string Name of a class that is used to resolve flags to codes.
*/
protected static $flagsResolver = '';
/**
* @var string Name of a class that is used to resolve styles to codes.
*/
protected static $stylesResolver = '';
/**
* @var int Flags to set.
*/
protected $flags = 0;
/**
* @var int|null The code for the currently specified font color.
*/
protected $font = null;
/**
* @var int|null The code for the currently specified background color.
*/
protected $backgorund = null;
/**
* @var bool[] Array with the status of each style.
*/
protected $styles = array();
/**
* @var string|null The string to write to console to get the specified
* styling. NULL when the string needs to be regenerated.
*/
protected $sequence = null;
/**
* Fills the list of valid fonts and backgrounds.
*
* Classes extending this one that wish to add additional valid colors,
* flags or styles should call this method in their own constructor BEFORE
* calling the parent constructor.
*
* @param string $fonts Name of class, the constants of which are
* valid font colors.
* @param string $backgrounds Name of class, the constants of which are
* valid background colors.
* @param string $flags Name of class that resolves flags to codes.
* Must inheirt from {@link Flags}. Constants of this
* class are considered the valid flags, and the coresponding codes must
* be overriden at the static $flagCodes property.
* @param string $styles Name of class that resolves styles to codes.
* Must inherit from {@link Styles}. Constants of this class are
* considered the valid styles, and the corresponding off/on codes must
* be overriden at the static $styleCodes property.
*
* @return void
*/
protected static function fillVlidators(
$fonts,
$backgrounds,
$flags,
$styles
) {
if (empty(static::$validFonts)) {
$fonts = new ReflectionClass($fonts);
static::$validFonts = array_values(
array_unique($fonts->getConstants(), SORT_REGULAR)
);
}
if (empty(static::$validBackgorunds)) {
$bgs = new ReflectionClass($backgrounds);
static::$validBackgorunds = array_values(
array_unique($bgs->getConstants(), SORT_REGULAR)
);
}
if ('' === static::$flagsResolver) {
$base = __CLASS__ . '\Flags';
if ($base === $flags || is_subclass_of($flags, $base)) {
static::$flagsResolver = $flags;
}
}
if ('' === static::$stylesResolver) {
$base = __CLASS__ . '\Styles';
if ($base === $styles || is_subclass_of($styles, $base)) {
static::$stylesResolver = $styles;
}
}
}
/**
* Creates a new color.
*
* Note that leaving all arguments with their default values (and not
* applying styles) would result in a sequence that resets all settings to
* the console's defaults.
*
* @param int|null $font Initial font color.
* @param int|null $background Initial backgorund color.
* @param int $flags Initial flags.
*
* @see setFlags()
* @see setStyles()
* @see __toString()
*/
public function __construct(
$font = Fonts::KEEP,
$background = Backgrounds::KEEP,
$flags = Flags::NONE
) {
static::fillVlidators(
__CLASS__ . '\Fonts',
__CLASS__ . '\Backgrounds',
__CLASS__ . '\Flags',
__CLASS__ . '\Styles'
);
$this->setFont($font);
$this->setBackground($background);
$this->setFlags($flags);
}
/**
* Gets the font color.
*
* @return int|null $color The font color.
*/
public function getFont()
{
return $this->font;
}
/**
* Sets the font color.
*
* @param int|null $color The font color.
*
* @return $this
*/
public function setFont($color)
{
if (!in_array($color, static::$validFonts, true)) {
throw new UnexpectedValueException(
'Invalid font supplied.',
UnexpectedValueException::CODE_FONT
);
}
$this->font = $color;
$this->sequence = null;
return $this;
}
/**
* Gets the background color.
*
* @return int|null $color The background color.
*/
public function getBackground()
{
return $this->backgorund;
}
/**
* Sets the background color.
*
* @param int|null $color The background color.
*
* @return $this
*/
public function setBackground($color)
{
if (!in_array($color, static::$validBackgorunds, true)) {
throw new UnexpectedValueException(
'Invalid background supplied.',
UnexpectedValueException::CODE_BACKGROUND
);
}
$this->backgorund = $color;
$this->sequence = null;
return $this;
}
/**
* Gets the flags.
*
* @return int The currently set flags.
*/
public function getFlags()
{
return $this->flags;
}
/**
* Sets the flags.
*
* Sets the flags to apply in the sequence. Note that flags are applied
* before all other settings, in ascending order of the constant values.
*
* @param int $flags The new flags to set. Unknown flags will be ignored
* when forming the sequence, but will be visible with
* {@link getFlags()} non the less.
*
* @return $this
*/
public function setFlags($flags)
{
$this->flags = (int)$flags;
$this->sequence = null;
return $this;
}
/**
* Gets styles.
*
* @param int|null $style A single style to get the status of,
* or {@link Styles::ALL} to get all styles in an array.
*
* @return bool|null|bool[] A single style status, or
* an array of status if $style is {@link Styles::ALL}.
*/
public function getStyles($style = Styles::ALL)
{
if (Styles::ALL === $style) {
return $this->styles;
}
return isset($this->styles[$style]) ? $this->styles[$style] : null;
}
/**
* Sets styles.
*
* Sets styles matched to a specified state.
*
* @param int|null $styles Bitmask of styles to set. You can also use the
* constant {@link Styles::ALL} (only) to set all known styles.
* Unknown styles will be ignored.
* @param bool|null $state The state to set the matched styles in.
* TRUE to enable them,
* FLASE to disable them,
* NULL to remove the setting for them (in effect using whatever the
* console had before the sequence was applied).
*
* @return $this
*/
public function setStyles($styles, $state)
{
$matchingStyles = call_user_func(
array(static::$stylesResolver, 'match'),
$styles
);
if (null === $state) {
foreach ($matchingStyles as $style) {
unset($this->styles[$style]);
}
} else {
$state = (bool)$state;
foreach ($matchingStyles as $style) {
$this->styles[$style] = $state;
}
ksort($this->styles);
}
$this->sequence = null;
return $this;
}
/**
* Get the console escaping sequence.
*
* This is a magic PHP method that will be called when you use the object in
* a string context or otherwise explicitly cast it to a string.
*
* It generates the escape sequence and returns it.
* For the sake of performance, the escape sequence is cached, and is only
* regenerated when a setter has been previously called.
*
* @return string The string to write to console to get the specified
* styling.
*/
public function __toString()
{
if (null === $this->sequence) {
$seq = "\033[";
$flags = implode(
';',
call_user_func(
array(static::$flagsResolver, 'getCodes'),
$this->flags
)
);
if ('' !== $flags) {
$seq .= $flags . ';';
}
if (Fonts::KEEP !== $this->font) {
$seq .= "{$this->font};";
}
if (Backgrounds::KEEP !== $this->backgorund) {
$seq .= "{$this->backgorund};";
}
foreach ($this->styles as $style => $state) {
$seq .= call_user_func(
array(static::$stylesResolver, 'getCode'),
$style,
$state
) . ';';
}
$this->sequence = rtrim($seq, ';') . 'm';
}
return $this->sequence;
}
}

View File

@ -0,0 +1,136 @@
<?php
/**
* Backgrounds class for PEAR2_Console_Color.
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Ivo Nascimento <ivo@o8o.com.br>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
/**
* This class has the possibles values to a Background Color.
*
* @category Console
* @package PEAR2_Console_Color
* @author Ivo Nascimento <ivo@o8o.com.br>
* @copyright 2011 Ivo Nascimento
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
abstract class Backgrounds
{
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to specify that
* the background color already in effect should be kept.
*/
const KEEP = null;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to black/grey (implmementation defined).
*/
const BLACK = 40;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to black/grey (implementation defined).
*/
const GREY = 40;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to maroon/red (implementation defined).
*/
const MAROON = 41;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to maroon/red (implementation defined).
*/
const RED = 41;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to green/lime (implementation defined).
*/
const GREEN = 42;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to green/lime (implementation defined).
*/
const LIME = 42;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to brown/yellow (implementation defined).
*/
const BROWN = 43;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to brown/yellow (implementation defined).
*/
const YELLOW = 43;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to navy/blue (implementation defined).
*/
const NAVY = 44;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to navy/blue (implementation defined).
*/
const BLUE = 44;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to purple/magenta (implementation defined).
*/
const PURPLE = 45;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to purple/magenta (implementation defined).
*/
const MAGENTA = 45;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to teal/cyan (implementation defined).
*/
const TEAL = 46;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to teal/cyan (implementation defined).
*/
const CYAN = 46;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to silver/white (implementation defined).
*/
const SILVER = 47;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to silver/white (implementation defined).
*/
const WHITE = 47;
/**
* Used at {@link \PEAR2\Console\Color::setBackground()} to set the
* background color to whatever the default one is.
*/
const RESET = 49;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Exception class for PEAR2_Console_Color.
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
/**
* Exception class for PEAR2_Console_Color.
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
interface Exception
{
}

View File

@ -0,0 +1,88 @@
<?php
/**
* Flags class for PEAR2_Console_Color
* Mappping the names of Font Style to your values.
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
use ReflectionClass;
/**
* This class has the possibles flags to a color setting.
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
abstract class Flags
{
/**
* Used at {@link \PEAR2\Console\Color::setFlags()} to specify that no
* flags should be applied.
*/
const NONE = 0;
/**
* Used at {@link \PEAR2\Console\Color::setFlags()} as part of a bitmask.
* If specified, resets all color and style information before applying
* everything else.
*/
const RESET = 1;
/**
* Used at {@link \PEAR2\Console\Color::setFlags()} as part of a bitmask.
* If specified, inverses the font and background colors, before letting
* the remaining settings further modify things.
* If specified together with {@link self::RESET}, takes effect AFTER the
* reset.
*/
const INVERSE = 2;
/**
* @var int[] Array with the flag as a key, and the corresponding code as a
* value.
*/
protected static $flagCodes = array(
self::RESET => 0,
self::INVERSE => 7
);
/**
* Gets the codes for a flag set.
*
* @param int $flags The flags to get the codes for.
*
* @return int[] The codes for the flags specified, in ascending order,
* based on the flag constants' values.
*/
final public static function getCodes($flags)
{
if (self::NONE === $flags) {
return array();
}
$result = array();
$flagsClass = new ReflectionClass(get_called_class());
$validFlags = array_values(
array_unique($flagsClass->getConstants(), SORT_NUMERIC)
);
foreach ($validFlags as $flag) {
if ($flags & $flag) {
$result[] = static::$flagCodes[$flag];
}
}
return $result;
}
}

View File

@ -0,0 +1,136 @@
<?php
/**
* Font class for PEAR2_Console_Color
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Ivo Nascimento <ivo@o8o.com.br>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
/**
* This class has the possibles values to a Font Color.
*
* @category Console
* @package PEAR2_Console_Color
* @author Ivo Nascimento <ivo@o8o.com.br>
* @copyright 2011 Ivo Nascimento
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
abstract class Fonts
{
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to specify that
* the font color already in effect should be kept.
*/
const KEEP = null;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to black/grey (implementation defined).
*/
const BLACK = 30;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to black/grey (implementation defined).
*/
const GREY = 30;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to maroon/red (implementation defined).
*/
const MAROON = 31;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to maroon/red (implementation defined).
*/
const RED = 31;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to green/lime (implementation defined).
*/
const LIME = 32;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to green/lime (implementation defined).
*/
const GREEN = 32;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to brown/yellow (implementation defined).
*/
const BROWN = 33;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to brown/yellow (implementation defined).
*/
const YELLOW = 33;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to navy/blue (implementation defined).
*/
const NAVY = 34;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to navy/blue (implementation defined).
*/
const BLUE = 34;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to purple/magenta (implementation defined).
*/
const PURPLE = 35;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to purple/magenta (implementation defined).
*/
const MAGENTA = 35;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to teal/cyan (implementation defined).
*/
const TEAL = 36;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to teal/cyan (implementation defined).
*/
const CYAN = 36;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to silver/white (implementation defined).
*/
const SILVER = 37;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to silver/white (implementation defined).
*/
const WHITE = 37;
/**
* Used at {@link \PEAR2\Console\Color::setFont()} to set the
* font color to whatever the default one is.
*/
const RESET = 39;
}

View File

@ -0,0 +1,130 @@
<?php
/**
* Styles class for PEAR2_Console_Color.
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
use ReflectionClass;
/**
* This class has the possibles values to a Font Style.
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
abstract class Styles
{
/**
* Used in {@link \PEAR2\Console\Color::setStyles()} to match all styles.
*/
const ALL = null;
/**
* Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask.
* If specified, matches the bold style.
* When this style is enabled, the font is bolder.
* With ANSICON, the font color becomes more intense (but not bolder).
*/
const BOLD = 1;
/**
* Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask.
* If specified, matches the underline style.
* When this style is enabled, the font is underlined.
* With ANSICON, the background color becomes more intense
* (and the font is not underlined), same as {@link self::BLINK}.
*/
const UNDERLINE = 2;
/**
* Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask.
* If specified, matches the blink style.
* When this style is enabled, the font color switches between its regular
* color and the background color at regular (implementation defined)
* intervals, creating the illusion of a blinking text.
* With ANSICON, the background color becomes more intense
* (and the font is not blinking), same as with {@link self::UNDERLINE}.
*/
const BLINK = 4;
/**
* Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask.
* If specified, matches the concealed style.
* When this style is enabled, the font color becomes the background color,
* rendering the text invisible. This style is particularly useful for
* implementations where simply setting the same color and background color
* would not necesarily provide a fully invisibile text (e.g. ANSICON).
*/
const CONCEALED = 8;
/**
* @var (int[])[] An array describing the codes for the styles.
* Each array key is the style's constant, and each value is an array
* where the first member is the disable code, and the second is the
* enable code.
*/
protected static $styleCodes = array(
self::BOLD => array(22, 1),
self::UNDERLINE => array(24, 4),
self::BLINK => array(25, 5),
self::CONCEALED => array(28, 8)
);
/**
* Get style constants.
*
* @param int|null $styles Bitmask of styles to match.
* You can also use {@link self::ALL} (only) to get all styles.
*
* @return int[] Matching style constants.
*/
final public static function match($styles)
{
$flagsClass = new ReflectionClass(get_called_class());
$validStyles = array_values(
array_unique($flagsClass->getConstants(), SORT_NUMERIC)
);
unset($validStyles[array_search(self::ALL, $validStyles, true)]);
if (self::ALL === $styles) {
return $validStyles;
}
$styles = (int)$styles;
$result = array();
foreach ($validStyles as $flag) {
if ($styles & $flag) {
$result[] = $flag;
}
}
return $result;
}
/**
* Gets the code for a style.
*
* @param int $style The style to get the code for.
* @param bool $state The state to get code for.
* TRUE for the enabled state codes,
* FALSE for the disabled state codes.
*
* @return int The code for the flag specified.
*/
final public static function getCode($style, $state)
{
return static::$styleCodes[$style][(int)(bool)$state];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Exception class for PEAR2_Console_Color.
*
* PHP version 5.3
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0
* @link http://pear2.php.net/PEAR2_Console_Color
*/
namespace PEAR2\Console\Color;
use UnexpectedValueException as U;
/**
* Exception class for PEAR2_Console_Color.
*
* @category Console
* @package PEAR2_Console_Color
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Ivo Nascimento
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Console_Color
*/
class UnexpectedValueException extends U implements Exception
{
/**
* Used when an unexpected font value is supplied.
*/
const CODE_FONT = 1;
/**
* Used when an unexpected background value is supplied.
*/
const CODE_BACKGROUND = 2;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Class that represent an option action.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
abstract class Action
{
// Properties {{{
/**
* A reference to the result instance.
*
* @var PEAR2\Console\CommandLine_Result $result The result instance
*/
protected $result;
/**
* A reference to the option instance.
*
* @var PEAR2\Console\CommandLine_Option $option The action option
*/
protected $option;
/**
* A reference to the parser instance.
*
* @var PEAR2\Console\CommandLine $parser The parser
*/
protected $parser;
// }}}
// __construct() {{{
/**
* Constructor
*
* @param PEAR2\Console\CommandLine_Result $result The result instance
* @param PEAR2\Console\CommandLine_Option $option The action option
* @param PEAR2\Console\CommandLine $parser The current parser
*
* @return void
*/
public function __construct($result, $option, $parser)
{
$this->result = $result;
$this->option = $option;
$this->parser = $parser;
}
// }}}
// getResult() {{{
/**
* Convenience method to retrieve the value of result->options[name].
*
* @return mixed The result value or null
*/
public function getResult()
{
if (isset($this->result->options[$this->option->name])) {
return $this->result->options[$this->option->name];
}
return null;
}
// }}}
// format() {{{
/**
* Allow a value to be pre-formatted prior to being used in a choices test.
* Setting $value to the new format will keep the formatting.
*
* @param mixed &$value The value to format
*
* @return mixed The formatted value
*/
public function format(&$value)
{
return $value;
}
// }}}
// setResult() {{{
/**
* Convenience method to assign the result->options[name] value.
*
* @param mixed $result The result value
*
* @return void
*/
public function setResult($result)
{
$this->result->options[$this->option->name] = $result;
}
// }}}
// execute() {{{
/**
* Executes the action with the value entered by the user.
* All children actions must implement this method.
*
* @param mixed $value The option value
* @param array $params An optional array of parameters
*
* @return string
*/
abstract public function execute($value = false, $params = array());
// }}}
}

View File

@ -0,0 +1,86 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the Callback action.
*
* The result option array entry value is set to the return value of the
* callback defined in the option.
*
* There are two steps to defining a callback option:
* - define the option itself using the callback action
* - write the callback; this is a function (or method) that takes five
* arguments, as described below.
*
* All callbacks are called as follows:
* <code>
* callable_func(
* $value, // the value of the option
* $option_instance, // the option instance
* $result_instance, // the result instance
* $parser_instance, // the parser instance
* $params // an array of params as specified in the option
* );
* </code>
* and *must* return the option value.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Callback extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The value of the option
* @param array $params An optional array of parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$this->setResult(
call_user_func(
$this->option->callback,
$value,
$this->option,
$this->result,
$this->parser,
$params
)
);
}
// }}}
}

View File

@ -0,0 +1,84 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the Version action.
*
* The execute methode add 1 to the value of the result option array entry.
* The value is incremented each time the option is found, for example
* with an option defined like that:
*
* <code>
* $parser->addOption(
* 'verbose',
* array(
* 'short_name' => '-v',
* 'action' => 'Counter'
* )
* );
* </code>
* If the user type:
* <code>
* $ script.php -v -v -v
* </code>
* or:
* <code>
* $ script.php -vvv
* </code>
* the verbose variable will be set to to 3.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Counter extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An optional array of parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$result = $this->getResult();
if ($result === null) {
$result = 0;
}
$this->setResult(++$result);
}
// }}}
}

View File

@ -0,0 +1,58 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the Help action, a special action that displays the
* help message, telling the user how to use the program.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Help extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An optional array of parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
return $this->parser->displayUsage();
}
// }}}
}

View File

@ -0,0 +1,69 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version CVS: $Id: List.php,v 1.2 2009/02/27 08:03:17 izi Exp $
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Class that represent the List action, a special action that simply output an
* array as a list.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Action_List extends Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
* Possible parameters are:
* - message: an alternative message to display instead of the default
* message,
* - delimiter: an alternative delimiter instead of the comma,
* - post: a string to append after the message (default is the new line
* char).
*
* @param mixed $value The option value
* @param array $params An optional array of parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$list = isset($params['list']) ? $params['list'] : array();
$msg = isset($params['message'])
? $params['message']
: $this->parser->message_provider->get('LIST_DISPLAYED_MESSAGE');
$del = isset($params['delimiter']) ? $params['delimiter'] : ', ';
$post = isset($params['post']) ? $params['post'] : "\n";
$this->parser->outputter->stdout($msg . implode($del, $list) . $post);
exit(0);
}
// }}}
}

View File

@ -0,0 +1,90 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the Password action, a special action that allow the
* user to specify the password on the commandline or to be prompted for
* entering it.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Password extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$this->setResult(empty($value) ? $this->_promptPassword() : $value);
}
// }}}
// _promptPassword() {{{
/**
* Prompts the password to the user without echoing it.
*
* @return string
* @todo not echo-ing the password does not work on windows is there a way
* to make this work ?
*/
private function _promptPassword()
{
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
fwrite(
STDOUT,
$this->parser->message_provider->get('PASSWORD_PROMPT_ECHO')
);
@flock(STDIN, LOCK_EX);
$passwd = fgets(STDIN);
@flock(STDIN, LOCK_UN);
} else {
fwrite(STDOUT, $this->parser->message_provider->get('PASSWORD_PROMPT'));
// disable echoing
system('stty -echo');
@flock(STDIN, LOCK_EX);
$passwd = fgets(STDIN);
@flock(STDIN, LOCK_UN);
system('stty echo');
}
return trim($passwd);
}
// }}}
}

View File

@ -0,0 +1,76 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreArray action.
*
* The execute method appends the value of the option entered by the user to
* the result option array entry.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreArray extends CommandLine\Action
{
// Protected properties {{{
/**
* Force a clean result when first called, overriding any defaults assigned.
*
* @var object $firstPass First time this action has been called.
*/
protected $firstPass = true;
// }}}
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An optional array of parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$result = $this->getResult();
if (null === $result || $this->firstPass) {
$result = array();
$this->firstPass = false;
}
$result[] = $value;
$this->setResult($result);
}
// }}}
}

View File

@ -0,0 +1,62 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreFalse action.
*
* The execute method store the boolean 'false' in the corrsponding result
* option array entry (the value is true if the option is not present in the
* command line entered by the user).
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreFalse extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$this->setResult(false);
}
// }}}
}

View File

@ -0,0 +1,73 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreFloat action.
*
* The execute method store the value of the option entered by the user as a
* float in the result option array entry, if the value passed is not a float
* an Exception is raised.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreFloat extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
* @throws PEAR2\Console\CommandLine\Exception
*/
public function execute($value = false, $params = array())
{
if (!is_numeric($value)) {
throw CommandLine\Exception::factory(
'OPTION_VALUE_TYPE_ERROR',
array(
'name' => $this->option->name,
'type' => 'float',
'value' => $value
),
$this->parser
);
}
$this->setResult((float)$value);
}
// }}}
}

View File

@ -0,0 +1,73 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreInt action.
*
* The execute method store the value of the option entered by the user as an
* integer in the result option array entry, if the value passed is not an
* integer an Exception is raised.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreInt extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
* @throws PEAR2\Console\CommandLine\Exception
*/
public function execute($value = false, $params = array())
{
if (!is_numeric($value)) {
throw CommandLine\Exception::factory(
'OPTION_VALUE_TYPE_ERROR',
array(
'name' => $this->option->name,
'type' => 'int',
'value' => $value
),
$this->parser
);
}
$this->setResult((int)$value);
}
// }}}
}

View File

@ -0,0 +1,60 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreString action.
*
* The execute method store the value of the option entered by the user as a
* string in the result option array entry.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreString extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$this->setResult((string)$value);
}
// }}}
}

View File

@ -0,0 +1,61 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the StoreTrue action.
*
* The execute method store the boolean 'true' in the corrsponding result
* option array entry (the value is false if the option is not present in the
* command line entered by the user).
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class StoreTrue extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
$this->setResult(true);
}
// }}}
}

View File

@ -0,0 +1,58 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine\Action;
use PEAR2\Console\CommandLine;
/**
* Class that represent the Version action, a special action that displays the
* version string of the program.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Version extends CommandLine\Action
{
// execute() {{{
/**
* Executes the action with the value entered by the user.
*
* @param mixed $value The option value
* @param array $params An array of optional parameters
*
* @return string
*/
public function execute($value = false, $params = array())
{
return $this->parser->displayVersion();
}
// }}}
}

View File

@ -0,0 +1,94 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Class that represent a command line argument.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Argument extends Element
{
// Public properties {{{
/**
* Setting this to true will tell the parser that the argument expects more
* than one argument and that argument values should be stored in an array.
*
* @var boolean $multiple Whether the argument expects multiple values
*/
public $multiple = false;
/**
* Setting this to true will tell the parser that the argument is optional
* and can be ommited.
* Note that it is not a good practice to make arguments optional, it is
* the role of the options to be optional, by essence.
*
* @var boolean $optional Whether the argument is optional or not.
*/
public $optional = false;
// }}}
// validate() {{{
/**
* Validates the argument instance.
*
* @return void
* @throws PEAR2\Console\CommandLine\Exception
* @todo use exceptions
*/
public function validate()
{
// check if the argument name is valid
if (!preg_match(
'/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/',
$this->name
)
) {
\PEAR2\Console\CommandLine::triggerError(
'argument_bad_name',
E_USER_ERROR,
array('{$name}' => $this->name)
);
}
if (!$this->optional && $this->default !== null) {
\PEAR2\Console\CommandLine::triggerError(
'argument_no_default',
E_USER_ERROR
);
}
parent::validate();
}
// }}}
}

View File

@ -0,0 +1,72 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Class that represent a command with option and arguments.
*
* This class exist just to clarify the interface but at the moment it is
* strictly identical to PEAR2\Console\CommandLine class, it could change in the
* future though.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Command extends \PEAR2\Console\CommandLine
{
// Public properties {{{
/**
* An array of aliases for the subcommand.
*
* @var array $aliases Aliases for the subcommand.
*/
public $aliases = array();
// }}}
// __construct() {{{
/**
* Constructor.
*
* @param array $params An optional array of parameters
*
* @return void
*/
public function __construct($params = array())
{
if (isset($params['aliases'])) {
$this->aliases = $params['aliases'];
}
parent::__construct($params);
}
// }}}
}

View File

@ -0,0 +1,67 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @author Michael Gauthier <mike@silverorange.com>
* @copyright 2007 David JEAN LOUIS, 2009 silverorange
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version CVS: $Id: CustomMessageProvider.php 282427 2009-06-19 10:22:48Z izi $
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 1.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Common interfacefor message providers that allow overriding with custom
* messages
*
* Message providers may optionally implement this interface.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @author Michael Gauthier <mike@silverorange.com>
* @copyright 2007 David JEAN LOUIS, 2009 silverorange
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Interface available since release 1.1.0
*/
interface CustomMessageProvider
{
// getWithCustomMesssages() {{{
/**
* Retrieves the given string identifier corresponding message.
*
* For a list of identifiers please see the provided default message
* provider.
*
* @param string $code The string identifier of the message
* @param array $vars An array of template variables
* @param array $messages An optional array of messages to use. Array
* indexes are message codes.
*
* @return string
* @see PEAR2\Console\CommandLine_MessageProvider
* @see PEAR2\Console\CommandLine_MessageProvider_Default
*/
public function getWithCustomMessages(
$code, $vars = array(), $messages = array()
);
// }}}
}

View File

@ -0,0 +1,151 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Class that represent a command line element (an option, or an argument).
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
abstract class Element
{
// Public properties {{{
/**
* The element name.
*
* @var string $name Element name
*/
public $name;
/**
* The name of variable displayed in the usage message, if no set it
* defaults to the "name" property.
*
* @var string $help_name Element "help" variable name
*/
public $help_name;
/**
* The element description.
*
* @var string $description Element description
*/
public $description;
/**
* The default value of the element if not provided on the command line.
*
* @var mixed $default Default value of the option.
*/
public $default;
/**
* Custom errors messages for this element
*
* This array is of the form:
* <code>
* <?php
* array(
* $messageName => $messageText,
* $messageName => $messageText,
* ...
* );
* ?>
* </code>
*
* If specified, these messages override the messages provided by the
* default message provider. For example:
* <code>
* <?php
* $messages = array(
* 'ARGUMENT_REQUIRED' => 'The argument foo is required.',
* );
* ?>
* </code>
*
* @var array
* @see PEAR2\Console\CommandLine_MessageProvider_Default
*/
public $messages = array();
// }}}
// __construct() {{{
/**
* Constructor.
*
* @param string $name The name of the element
* @param array $params An optional array of parameters
*
* @return void
*/
public function __construct($name = null, $params = array())
{
$this->name = $name;
foreach ($params as $attr => $value) {
if (property_exists($this, $attr)) {
$this->$attr = $value;
}
}
}
// }}}
// toString() {{{
/**
* Returns the string representation of the element.
*
* @return string The string representation of the element
* @todo use __toString() instead
*/
public function toString()
{
return $this->help_name;
}
// }}}
// validate() {{{
/**
* Validates the element instance and set it's default values.
*
* @return void
* @throws PEAR2\Console\CommandLine\Exception
*/
public function validate()
{
// if no help_name passed, default to name
if ($this->help_name == null) {
$this->help_name = $this->name;
}
}
// }}}
}

View File

@ -0,0 +1,90 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
use Exception as E;
/**
* Class for exceptions raised by the PEAR2\Console\CommandLine package.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Exception extends E
{
// Codes constants {{{
/**#@+
* Exception code constants.
*/
const OPTION_VALUE_REQUIRED = 1;
const OPTION_VALUE_UNEXPECTED = 2;
const OPTION_VALUE_TYPE_ERROR = 3;
const OPTION_UNKNOWN = 4;
const ARGUMENT_REQUIRED = 5;
const INVALID_SUBCOMMAND = 6;
/**#@-*/
// }}}
// factory() {{{
/**
* Convenience method that builds the exception with the array of params by
* calling the message provider class.
*
* @param string $code The string identifier of the
* exception.
* @param array $params Array of template vars/values
* @param PEAR2\Console\CommandLine $parser An instance of the parser
* @param array $messages An optional array of messages
* passed to the message provider.
*
* @return PEAR2\Console\CommandLine\Exception The exception instance
*/
public static function factory(
$code, $params, $parser, array $messages = array()
) {
$provider = $parser->message_provider;
if ($provider instanceof CommandLine\CustomMessageProvider) {
$msg = $provider->getWithCustomMessages(
$code,
$params,
$messages
);
} else {
$msg = $provider->get($code, $params);
}
$const = '\PEAR2\Console\CommandLine\Exception::' . $code;
$code = defined($const) ? constant($const) : 0;
return new static($msg, $code);
}
// }}}
}

View File

@ -0,0 +1,57 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Message providers common interface, all message providers must implement
* this interface.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
interface MessageProvider
{
// get() {{{
/**
* Retrieves the given string identifier corresponding message.
* For a list of identifiers please see the provided default message
* provider.
*
* @param string $code The string identifier of the message
* @param array $vars An array of template variables
*
* @return string
* @see PEAR2\Console\CommandLine_MessageProvider_Default
*/
public function get($code, $vars=array());
// }}}
}

View File

@ -0,0 +1,143 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Lightweight class that manages messages used by PEAR2\Console\CommandLine package,
* allowing the developper to customize these messages, for example to
* internationalize a command line frontend.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class MessageProvider_Default
implements MessageProvider,
CustomMessageProvider
{
// Properties {{{
/**
* Associative array of messages
*
* @var array $messages
*/
protected $messages = array(
'OPTION_VALUE_REQUIRED' => 'Option "{$name}" requires a value.',
'OPTION_VALUE_UNEXPECTED' => 'Option "{$name}" does not expect a value (got "{$value}").',
'OPTION_VALUE_NOT_VALID' => 'Option "{$name}" must be one of the following: "{$choices}" (got "{$value}").',
'OPTION_VALUE_TYPE_ERROR' => 'Option "{$name}" requires a value of type {$type} (got "{$value}").',
'OPTION_AMBIGUOUS' => 'Ambiguous option "{$name}", can be one of the following: {$matches}.',
'OPTION_UNKNOWN' => 'Unknown option "{$name}".',
'ARGUMENT_REQUIRED' => 'You must provide at least {$argnum} argument{$plural}.',
'PROG_HELP_LINE' => 'Type "{$progname} --help" to get help.',
'PROG_VERSION_LINE' => '{$progname} version {$version}.',
'COMMAND_HELP_LINE' => 'Type "{$progname} <command> --help" to get help on specific command.',
'USAGE_WORD' => 'Usage',
'OPTION_WORD' => 'Options',
'ARGUMENT_WORD' => 'Arguments',
'COMMAND_WORD' => 'Commands',
'PASSWORD_PROMPT' => 'Password: ',
'PASSWORD_PROMPT_ECHO' => 'Password (warning: will echo): ',
'INVALID_CUSTOM_INSTANCE' => 'Instance does not implement the required interface',
'LIST_OPTION_MESSAGE' => 'lists valid choices for option {$name}',
'LIST_DISPLAYED_MESSAGE' => 'Valid choices are: ',
'INVALID_SUBCOMMAND' => 'Command "{$command}" is not valid.',
'SUBCOMMAND_REQUIRED' => 'Please enter one of the following command: {$commands}.',
);
// }}}
// get() {{{
/**
* Retrieve the given string identifier corresponding message.
*
* @param string $code The string identifier of the message
* @param array $vars An array of template variables
*
* @return string
*/
public function get($code, $vars = array())
{
if (!isset($this->messages[$code])) {
return 'UNKNOWN';
}
return $this->replaceTemplateVars($this->messages[$code], $vars);
}
// }}}
// getWithCustomMessages() {{{
/**
* Retrieve the given string identifier corresponding message.
*
* @param string $code The string identifier of the message
* @param array $vars An array of template variables
* @param array $messages An optional array of messages to use. Array
* indexes are message codes.
*
* @return string
*/
public function getWithCustomMessages(
$code, $vars = array(), $messages = array()
) {
// get message
if (isset($messages[$code])) {
$message = $messages[$code];
} elseif (isset($this->messages[$code])) {
$message = $this->messages[$code];
} else {
$message = 'UNKNOWN';
}
return $this->replaceTemplateVars($message, $vars);
}
// }}}
// replaceTemplateVars() {{{
/**
* Replaces template vars in a message
*
* @param string $message The message
* @param array $vars An array of template variables
*
* @return string
*/
protected function replaceTemplateVars($message, $vars = array())
{
$tmpkeys = array_keys($vars);
$keys = array();
foreach ($tmpkeys as $key) {
$keys[] = '{$' . $key . '}';
}
return str_replace($keys, array_values($vars), $message);
}
// }}}
}

View File

@ -0,0 +1,393 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
use PEAR2\Console;
/**
* Class that represent a commandline option.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Option extends Element
{
// Public properties {{{
/**
* The option short name (ex: -v).
*
* @var string $short_name Short name of the option
*/
public $short_name;
/**
* The option long name (ex: --verbose).
*
* @var string $long_name Long name of the option
*/
public $long_name;
/**
* The option action, defaults to "StoreString".
*
* @var string $action Option action
*/
public $action = 'StoreString';
/**
* An array of possible values for the option. If this array is not empty
* and the value passed is not in the array an exception is raised.
* This only make sense for actions that accept values of course.
*
* @var array $choices Valid choices for the option
*/
public $choices = array();
/**
* The callback function (or method) to call for an action of type
* Callback, this can be any callable supported by the php function
* call_user_func.
*
* Example:
*
* <code>
* $parser->addOption('myoption', array(
* 'short_name' => '-m',
* 'long_name' => '--myoption',
* 'action' => 'Callback',
* 'callback' => 'myCallbackFunction'
* ));
* </code>
*
* @var callable $callback The option callback
*/
public $callback;
/**
* An associative array of additional params to pass to the class
* corresponding to the action, this array will also be passed to the
* callback defined for an action of type Callback, Example:
*
* <code>
* // for a custom action
* $parser->addOption('myoption', array(
* 'short_name' => '-m',
* 'long_name' => '--myoption',
* 'action' => 'MyCustomAction',
* 'action_params' => array('foo'=>true, 'bar'=>false)
* ));
*
* // if the user type:
* // $ <yourprogram> -m spam
* // in your MyCustomAction class the execute() method will be called
* // with the value 'spam' as first parameter and
* // array('foo'=>true, 'bar'=>false) as second parameter
* </code>
*
* @var array $action_params Additional parameters to pass to the action
*/
public $action_params = array();
/**
* For options that expect an argument, this property tells the parser if
* the option argument is optional and can be ommited.
*
* @var bool $argumentOptional Whether the option arg is optional or not
*/
public $argument_optional = false;
/**
* For options that uses the "choice" property only.
* Adds a --list-<choice> option to the parser that displays the list of
* choices for the option.
*
* @var bool $add_list_option Whether to add a list option or not
*/
public $add_list_option = false;
// }}}
// Private properties {{{
/**
* When an action is called remember it to allow for multiple calls.
*
* @var object $action_instance Placeholder for action
*/
private $_action_instance = null;
// }}}
// __construct() {{{
/**
* Constructor.
*
* @param string $name The name of the option
* @param array $params An optional array of parameters
*
* @return void
*/
public function __construct($name = null, $params = array())
{
parent::__construct($name, $params);
if ($this->action == 'Password') {
// special case for Password action, password can be passed to the
// commandline or prompted by the parser
$this->argument_optional = true;
}
}
// }}}
// toString() {{{
/**
* Returns the string representation of the option.
*
* @param string $delim Delimiter to use between short and long option
*
* @return string The string representation of the option
* @todo use __toString() instead
*/
public function toString($delim = ", ")
{
$ret = '';
$padding = '';
if ($this->short_name != null) {
$ret .= $this->short_name;
if ($this->expectsArgument()) {
$ret .= ' ' . $this->help_name;
}
$padding = $delim;
}
if ($this->long_name != null) {
$ret .= $padding . $this->long_name;
if ($this->expectsArgument()) {
$ret .= '=' . $this->help_name;
}
}
return $ret;
}
// }}}
// expectsArgument() {{{
/**
* Returns true if the option requires one or more argument and false
* otherwise.
*
* @return bool Whether the option expects an argument or not
*/
public function expectsArgument()
{
if ($this->action == 'StoreTrue'
|| $this->action == 'StoreFalse'
|| $this->action == 'Help'
|| $this->action == 'Version'
|| $this->action == 'Counter'
|| $this->action == 'List'
) {
return false;
}
return true;
}
// }}}
// dispatchAction() {{{
/**
* Formats the value $value according to the action of the option and
* updates the passed PEAR2\Console\CommandLine_Result object.
*
* @param mixed $value The value to format
* @param PEAR2\Console\CommandLine_Result $result The result instance
* @param PEAR2\Console\CommandLine $parser The parser instance
*
* @return void
* @throws PEAR2\Console\CommandLine\Exception
*/
public function dispatchAction($value, $result, $parser)
{
$actionInfo = Console\CommandLine::$actions[$this->action];
$clsname = $actionInfo[0];
if ($this->_action_instance === null) {
$this->_action_instance = new $clsname($result, $this, $parser);
}
// check value is in option choices
if (!empty($this->choices)
&& !in_array(
$this->_action_instance->format($value),
$this->choices
)
) {
throw Console\CommandLine\Exception::factory(
'OPTION_VALUE_NOT_VALID',
array(
'name' => $this->name,
'choices' => implode('", "', $this->choices),
'value' => $value,
),
$parser,
$this->messages
);
}
$this->_action_instance->execute($value, $this->action_params);
}
// }}}
// validate() {{{
/**
* Validates the option instance.
*
* @return void
* @throws PEAR2\Console\CommandLine\Exception
* @todo use exceptions instead
*/
public function validate()
{
// check if the option name is valid
if (!preg_match(
'/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/',
$this->name
)
) {
Console\CommandLine::triggerError(
'option_bad_name',
E_USER_ERROR,
array('{$name}' => $this->name)
);
}
// call the parent validate method
parent::validate();
// a short_name or a long_name must be provided
if ($this->short_name == null && $this->long_name == null) {
Console\CommandLine::triggerError(
'option_long_and_short_name_missing',
E_USER_ERROR,
array('{$name}' => $this->name)
);
}
// check if the option short_name is valid
if ($this->short_name != null
&& !(preg_match('/^\-[a-zA-Z]{1}$/', $this->short_name))
) {
Console\CommandLine::triggerError(
'option_bad_short_name',
E_USER_ERROR,
array(
'{$name}' => $this->name,
'{$short_name}' => $this->short_name
)
);
}
// check if the option long_name is valid
if ($this->long_name != null
&& !preg_match('/^\-\-[a-zA-Z]+[a-zA-Z0-9_\-]*$/', $this->long_name)
) {
Console\CommandLine::triggerError(
'option_bad_long_name',
E_USER_ERROR,
array(
'{$name}' => $this->name,
'{$long_name}' => $this->long_name
)
);
}
// check if we have a valid action
if (!is_string($this->action)) {
Console\CommandLine::triggerError(
'option_bad_action',
E_USER_ERROR,
array('{$name}' => $this->name)
);
}
if (!isset(Console\CommandLine::$actions[$this->action])) {
Console\CommandLine::triggerError(
'option_unregistered_action',
E_USER_ERROR,
array(
'{$action}' => $this->action,
'{$name}' => $this->name
)
);
}
// if the action is a callback, check that we have a valid callback
if ($this->action == 'Callback' && !is_callable($this->callback)) {
Console\CommandLine::triggerError(
'option_invalid_callback',
E_USER_ERROR,
array('{$name}' => $this->name)
);
}
}
// }}}
// setDefaults() {{{
/**
* Set the default value according to the configured action.
*
* Note that for backward compatibility issues this method is only called
* when the 'force_options_defaults' is set to true, it will become the
* default behaviour in the next major release of PEAR2\Console\CommandLine.
*
* @return void
*/
public function setDefaults()
{
if ($this->default !== null) {
// already set
return;
}
switch ($this->action) {
case 'Counter':
case 'StoreInt':
$this->default = 0;
break;
case 'StoreFloat':
$this->default = 0.0;
break;
case 'StoreArray':
$this->default = array();
break;
case 'StoreTrue':
$this->default = false;
break;
case 'StoreFalse':
$this->default = true;
break;
default:
return;
}
}
// }}}
}

View File

@ -0,0 +1,64 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Outputters common interface, all outputters must implement this interface.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
interface Outputter
{
// stdout() {{{
/**
* Processes the output for a message that should be displayed on STDOUT.
*
* @param string $msg The message to output
*
* @return void
*/
public function stdout($msg);
// }}}
// stderr() {{{
/**
* Processes the output for a message that should be displayed on STDERR.
*
* @param string $msg The message to output
*
* @return void
*/
public function stderr($msg);
// }}}
}

View File

@ -0,0 +1,78 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* PEAR2\Console\CommandLine default Outputter.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Outputter_Default implements Outputter
{
// stdout() {{{
/**
* Writes the message $msg to STDOUT.
*
* @param string $msg The message to output
*
* @return void
*/
public function stdout($msg)
{
if (defined('STDOUT')) {
fwrite(STDOUT, $msg);
} else {
echo $msg;
}
}
// }}}
// stderr() {{{
/**
* Writes the message $msg to STDERR.
*
* @param string $msg The message to output
*
* @return void
*/
public function stderr($msg)
{
if (defined('STDERR')) {
fwrite(STDERR, $msg);
} else {
echo $msg;
}
}
// }}}
}

View File

@ -0,0 +1,72 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* Renderers common interface, all renderers must implement this interface.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
interface Renderer
{
// usage() {{{
/**
* Returns the full usage message.
*
* @return string The usage message
*/
public function usage();
// }}}
// error() {{{
/**
* Returns a formatted error message.
*
* @param string $error The error message to format
*
* @return string The error string
*/
public function error($error);
// }}}
// version() {{{
/**
* Returns the program version string.
*
* @return string The version string
*/
public function version();
// }}}
}

View File

@ -0,0 +1,441 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
*/
namespace PEAR2\Console\CommandLine;
/**
* PEAR2\Console\CommandLine default renderer.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Renderer_Default implements Renderer
{
// Properties {{{
/**
* Integer that define the max width of the help text.
*
* @var integer $line_width Line width
*/
public $line_width = 75;
/**
* Integer that define the max width of the help text.
*
* @var integer $line_width Line width
*/
public $options_on_different_lines = false;
/**
* An instance of PEAR2\Console\CommandLine.
*
* @var PEAR2\Console\CommandLine $parser The parser
*/
public $parser = false;
// }}}
// __construct() {{{
/**
* Constructor.
*
* @param object $parser A PEAR2\Console\CommandLine instance
*
* @return void
*/
public function __construct($parser = false)
{
$this->parser = $parser;
}
// }}}
// usage() {{{
/**
* Returns the full usage message.
*
* @return string The usage message
*/
public function usage()
{
$ret = '';
if (!empty($this->parser->description)) {
$ret .= $this->description() . "\n\n";
}
$ret .= $this->usageLine() . "\n";
if (count($this->parser->commands) > 0) {
$ret .= $this->commandUsageLine() . "\n";
}
if (count($this->parser->options) > 0) {
$ret .= "\n" . $this->optionList() . "\n";
}
if (count($this->parser->args) > 0) {
$ret .= "\n" . $this->argumentList() . "\n";
}
if (count($this->parser->commands) > 0) {
$ret .= "\n" . $this->commandList() . "\n";
}
$ret .= "\n";
return $ret;
}
// }}}
// error() {{{
/**
* Returns a formatted error message.
*
* @param string $error The error message to format
*
* @return string The error string
*/
public function error($error)
{
$ret = 'Error: ' . $error . "\n";
if ($this->parser->add_help_option) {
$name = $this->name();
$ret .= $this->wrap(
$this->parser->message_provider->get(
'PROG_HELP_LINE',
array('progname' => $name)
)
) . "\n";
if (count($this->parser->commands) > 0) {
$ret .= $this->wrap(
$this->parser->message_provider->get(
'COMMAND_HELP_LINE',
array('progname' => $name)
)
) . "\n";
}
}
return $ret;
}
// }}}
// version() {{{
/**
* Returns the program version string.
*
* @return string The version string
*/
public function version()
{
return $this->parser->message_provider->get(
'PROG_VERSION_LINE',
array(
'progname' => $this->name(),
'version' => $this->parser->version
)
) . "\n";
}
// }}}
// name() {{{
/**
* Returns the full name of the program or the sub command
*
* @return string The name of the program
*/
protected function name()
{
$name = $this->parser->name;
$parent = $this->parser->parent;
while ($parent) {
if (count($parent->options) > 0) {
$name = '['
. strtolower(
$this->parser->message_provider->get(
'OPTION_WORD',
array('plural' => 's')
)
) . '] ' . $name;
}
$name = $parent->name . ' ' . $name;
$parent = $parent->parent;
}
return $this->wrap($name);
}
// }}}
// description() {{{
/**
* Returns the command line description message.
*
* @return string The description message
*/
protected function description()
{
return $this->wrap($this->parser->description);
}
// }}}
// usageLine() {{{
/**
* Returns the command line usage message
*
* @return string the usage message
*/
protected function usageLine()
{
$usage = $this->parser->message_provider->get('USAGE_WORD') . ":\n";
$ret = $usage . ' ' . $this->name();
if (count($this->parser->options) > 0) {
$ret .= ' ['
. strtolower($this->parser->message_provider->get('OPTION_WORD'))
. ']';
}
if (count($this->parser->args) > 0) {
foreach ($this->parser->args as $name=>$arg) {
$arg_str = $arg->help_name;
if ($arg->multiple) {
$arg_str .= '1 ' . $arg->help_name . '2 ...';
}
if ($arg->optional) {
$arg_str = '[' . $arg_str . ']';
}
$ret .= ' ' . $arg_str;
}
}
return $this->columnWrap($ret, 2);
}
// }}}
// commandUsageLine() {{{
/**
* Returns the command line usage message for subcommands.
*
* @return string The usage line
*/
protected function commandUsageLine()
{
if (count($this->parser->commands) == 0) {
return '';
}
$ret = ' ' . $this->name();
if (count($this->parser->options) > 0) {
$ret .= ' ['
. strtolower($this->parser->message_provider->get('OPTION_WORD'))
. ']';
}
$ret .= " <command>";
$hasArgs = false;
$hasOptions = false;
foreach ($this->parser->commands as $command) {
if (!$hasArgs && count($command->args) > 0) {
$hasArgs = true;
}
if (!$hasOptions && ($command->add_help_option
|| $command->add_version_option
|| count($command->options) > 0)
) {
$hasOptions = true;
}
}
if ($hasOptions) {
$ret .= ' [options]';
}
if ($hasArgs) {
$ret .= ' [args]';
}
return $this->columnWrap($ret, 2);
}
// }}}
// argumentList() {{{
/**
* Render the arguments list that will be displayed to the user, you can
* override this method if you want to change the look of the list.
*
* @return string The formatted argument list
*/
protected function argumentList()
{
$col = 0;
$args = array();
foreach ($this->parser->args as $arg) {
$argstr = ' ' . $arg->toString();
$args[] = array($argstr, $arg->description);
$ln = strlen($argstr);
if ($col < $ln) {
$col = $ln;
}
}
$ret = $this->parser->message_provider->get('ARGUMENT_WORD') . ":";
foreach ($args as $arg) {
$text = str_pad($arg[0], $col) . ' ' . $arg[1];
$ret .= "\n" . $this->columnWrap($text, $col+2);
}
return $ret;
}
// }}}
// optionList() {{{
/**
* Render the options list that will be displayed to the user, you can
* override this method if you want to change the look of the list.
*
* @return string The formatted option list
*/
protected function optionList()
{
$col = 0;
$options = array();
foreach ($this->parser->options as $option) {
$delim = $this->options_on_different_lines ? "\n" : ', ';
$optstr = $option->toString($delim);
$lines = explode("\n", $optstr);
$lines[0] = ' ' . $lines[0];
if (count($lines) > 1) {
$lines[1] = ' ' . $lines[1];
$ln = strlen($lines[1]);
} else {
$ln = strlen($lines[0]);
}
$options[] = array($lines, $option->description);
if ($col < $ln) {
$col = $ln;
}
}
$ret = $this->parser->message_provider->get('OPTION_WORD') . ":";
foreach ($options as $option) {
if (count($option[0]) > 1) {
$text = str_pad($option[0][1], $col) . ' ' . $option[1];
$pre = $option[0][0] . "\n";
} else {
$text = str_pad($option[0][0], $col) . ' ' . $option[1];
$pre = '';
}
$ret .= "\n" . $pre . $this->columnWrap($text, $col+2);
}
return $ret;
}
// }}}
// commandList() {{{
/**
* Render the command list that will be displayed to the user, you can
* override this method if you want to change the look of the list.
*
* @return string The formatted subcommand list
*/
protected function commandList()
{
$commands = array();
$col = 0;
foreach ($this->parser->commands as $cmdname=>$command) {
$cmdname = ' ' . $cmdname;
$commands[] = array($cmdname, $command->description, $command->aliases);
$ln = strlen($cmdname);
if ($col < $ln) {
$col = $ln;
}
}
$ret = $this->parser->message_provider->get('COMMAND_WORD') . ":";
foreach ($commands as $command) {
$text = str_pad($command[0], $col) . ' ' . $command[1];
if ($aliasesCount = count($command[2])) {
$pad = '';
$text .= ' (';
$text .= $aliasesCount > 1 ? 'aliases: ' : 'alias: ';
foreach ($command[2] as $alias) {
$text .= $pad . $alias;
$pad = ', ';
}
$text .= ')';
}
$ret .= "\n" . $this->columnWrap($text, $col+2);
}
return $ret;
}
// }}}
// wrap() {{{
/**
* Wraps the text passed to the method.
*
* @param string $text The text to wrap
* @param int $lw The column width (defaults to line_width property)
*
* @return string The wrapped text
*/
protected function wrap($text, $lw=null)
{
if ($this->line_width > 0) {
if ($lw === null) {
$lw = $this->line_width;
}
return wordwrap($text, $lw, "\n", false);
}
return $text;
}
// }}}
// columnWrap() {{{
/**
* Wraps the text passed to the method at the specified width.
*
* @param string $text The text to wrap
* @param int $cw The wrap width
*
* @return string The wrapped text
*/
protected function columnWrap($text, $cw)
{
$tokens = explode("\n", $this->wrap($text));
$ret = $tokens[0];
$text = trim(substr($text, strlen($ret)));
if (empty($text)) {
return $ret;
}
$chunks = $this->wrap($text, $this->line_width - $cw);
$tokens = explode("\n", $chunks);
foreach ($tokens as $token) {
if (!empty($token)) {
$ret .= "\n" . str_repeat(' ', $cw) . $token;
} else {
$ret .= "\n";
}
}
return $ret;
}
// }}}
}

View File

@ -0,0 +1,72 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
/**
* A lightweight class to store the result of the command line parsing.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class Result
{
// Public properties {{{
/**
* The result options associative array.
* Key is the name of the option and value its value.
*
* @var array $options Result options array
*/
public $options = array();
/**
* The result arguments array.
*
* @var array $args Result arguments array
*/
public $args = array();
/**
* Name of the command invoked by the user, false if no command invoked.
*
* @var string $command_name Result command name
*/
public $command_name = false;
/**
* A result instance for the subcommand.
*
* @var static $command Result instance for the subcommand
*/
public $command = false;
// }}}
}

View File

@ -0,0 +1,303 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* This file is part of the PEAR2\Console\CommandLine package.
*
* PHP version 5
*
* LICENSE: This source file is subject to the MIT license that is available
* through the world-wide-web at the following URI:
* http://opensource.org/licenses/mit-license.php
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.2.1
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since File available since release 0.1.0
* @filesource
*/
namespace PEAR2\Console\CommandLine;
use PEAR2\Console\CommandLine;
/**
* Parser for command line xml definitions.
*
* @category Console
* @package PEAR2\Console\CommandLine
* @author David JEAN LOUIS <izimobil@gmail.com>
* @copyright 2007-2009 David JEAN LOUIS
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://pear2.php.net/PEAR2_Console_CommandLine
* @since Class available since release 0.1.0
*/
class XmlParser
{
// parse() {{{
/**
* Parses the given xml definition file and returns a
* PEAR2\Console\CommandLine instance constructed with the xml data.
*
* @param string $xmlfile The xml file to parse
*
* @return PEAR2\Console\CommandLine A parser instance
*/
public static function parse($xmlfile)
{
if (!is_readable($xmlfile)) {
CommandLine::triggerError(
'invalid_xml_file',
E_USER_ERROR,
array('{$file}' => $xmlfile)
);
}
$doc = new \DomDocument();
$doc->load($xmlfile);
self::validate($doc);
$nodes = $doc->getElementsByTagName('command');
$root = $nodes->item(0);
return self::_parseCommandNode($root, true);
}
// }}}
// parseString() {{{
/**
* Parses the given xml definition string and returns a
* PEAR2\Console\CommandLine instance constructed with the xml data.
*
* @param string $xmlstr The xml string to parse
*
* @return PEAR2\Console\CommandLine A parser instance
*/
public static function parseString($xmlstr)
{
$doc = new \DomDocument();
$doc->loadXml($xmlstr);
self::validate($doc);
$nodes = $doc->getElementsByTagName('command');
$root = $nodes->item(0);
return self::_parseCommandNode($root, true);
}
// }}}
// validate() {{{
/**
* Validates the xml definition using Relax NG.
*
* @param DomDocument $doc The document to validate
*
* @return boolean Whether the xml data is valid or not.
* @throws PEAR2\Console\CommandLine\Exception
* @todo use exceptions only
*/
public static function validate($doc)
{
$rngfile = __DIR__
. '/../../../../data/pear2.php.net/PEAR2_Console_CommandLine/xmlschema.rng';
if (!is_file($rngfile)) {
$rngfile = __DIR__ . '/../../../../data/xmlschema.rng';
}
if (!is_readable($rngfile)) {
CommandLine::triggerError(
'invalid_xml_file',
E_USER_ERROR,
array('{$file}' => $rngfile)
);
}
return $doc->relaxNGValidate($rngfile);
}
// }}}
// _parseCommandNode() {{{
/**
* Parses the root command node or a command node and returns the
* constructed PEAR2\Console\CommandLine or PEAR2\Console\CommandLine_Command
* instance.
*
* @param DomDocumentNode $node The node to parse
* @param bool $isRootNode Whether it is a root node or not
*
* @return mixed PEAR2\Console\CommandLine or PEAR2\Console\CommandLine_Command
*/
private static function _parseCommandNode($node, $isRootNode = false)
{
if ($isRootNode) {
$obj = new CommandLine();
} else {
$obj = new CommandLine\Command();
}
foreach ($node->childNodes as $cNode) {
$cNodeName = $cNode->nodeName;
switch ($cNodeName) {
case 'name':
case 'description':
case 'version':
$obj->$cNodeName = trim($cNode->nodeValue);
break;
case 'add_help_option':
case 'add_version_option':
case 'force_posix':
$obj->$cNodeName = self::_bool(trim($cNode->nodeValue));
break;
case 'option':
$obj->addOption(self::_parseOptionNode($cNode));
break;
case 'argument':
$obj->addArgument(self::_parseArgumentNode($cNode));
break;
case 'command':
$obj->addCommand(self::_parseCommandNode($cNode));
break;
case 'aliases':
if (!$isRootNode) {
foreach ($cNode->childNodes as $subChildNode) {
if ($subChildNode->nodeName == 'alias') {
$obj->aliases[] = trim($subChildNode->nodeValue);
}
}
}
break;
case 'messages':
$obj->messages = self::_messages($cNode);
break;
default:
break;
}
}
return $obj;
}
// }}}
// _parseOptionNode() {{{
/**
* Parses an option node and returns the constructed
* PEAR2\Console\CommandLine_Option instance.
*
* @param DomDocumentNode $node The node to parse
*
* @return PEAR2\Console\CommandLine\Option The built option
*/
private static function _parseOptionNode($node)
{
$obj = new CommandLine\Option($node->getAttribute('name'));
foreach ($node->childNodes as $cNode) {
$cNodeName = $cNode->nodeName;
switch ($cNodeName) {
case 'choices':
foreach ($cNode->childNodes as $subChildNode) {
if ($subChildNode->nodeName == 'choice') {
$obj->choices[] = trim($subChildNode->nodeValue);
}
}
break;
case 'messages':
$obj->messages = self::_messages($cNode);
break;
default:
if (property_exists($obj, $cNodeName)) {
$obj->$cNodeName = trim($cNode->nodeValue);
}
break;
}
}
if ($obj->action == 'Password') {
$obj->argument_optional = true;
}
return $obj;
}
// }}}
// _parseArgumentNode() {{{
/**
* Parses an argument node and returns the constructed
* PEAR2\Console\CommandLine_Argument instance.
*
* @param DomDocumentNode $node The node to parse
*
* @return PEAR2\Console\CommandLine\Argument The built argument
*/
private static function _parseArgumentNode($node)
{
$obj = new CommandLine\Argument($node->getAttribute('name'));
foreach ($node->childNodes as $cNode) {
$cNodeName = $cNode->nodeName;
switch ($cNodeName) {
case 'description':
case 'help_name':
case 'default':
$obj->$cNodeName = trim($cNode->nodeValue);
break;
case 'multiple':
$obj->multiple = self::_bool(trim($cNode->nodeValue));
break;
case 'optional':
$obj->optional = self::_bool(trim($cNode->nodeValue));
break;
case 'messages':
$obj->messages = self::_messages($cNode);
break;
default:
break;
}
}
return $obj;
}
// }}}
// _bool() {{{
/**
* Returns a boolean according to true/false possible strings.
*
* @param string $str The string to process
*
* @return boolean
*/
private static function _bool($str)
{
return in_array((string)$str, array('true', '1', 'on', 'yes'));
}
// }}}
// _messages() {{{
/**
* Returns an array of custom messages for the element
*
* @param DOMNode $node The messages node to process
*
* @return array an array of messages
*
* @see PEAR2\Console\CommandLine::$messages
* @see PEAR2\Console\CommandLine_Element::$messages
*/
private static function _messages(DOMNode $node)
{
$messages = array();
foreach ($node->childNodes as $cNode) {
if ($cNode->nodeType == XML_ELEMENT_NODE) {
$name = $cNode->getAttribute('name');
$value = trim($cNode->nodeValue);
$messages[$name] = $value;
}
}
return $messages;
}
// }}}
}

View File

@ -0,0 +1,819 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Refers to transmitter direction constants.
*/
use PEAR2\Net\Transmitter\Stream as S;
/**
* Refers to the cryptography constants.
*/
use PEAR2\Net\Transmitter\NetworkStream as N;
/**
* Catches arbitrary exceptions at some points.
*/
use Exception as E;
/**
* A RouterOS client.
*
* Provides functionality for easily communicating with a RouterOS host.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class Client
{
/**
* Used in {@link static::isRequestActive()} to limit search only to
* requests that have a callback.
*/
const FILTER_CALLBACK = 1;
/**
* Used in {@link static::isRequestActive()} to limit search only to
* requests that use the buffer.
*/
const FILTER_BUFFER = 2;
/**
* Used in {@link static::isRequestActive()} to indicate no limit in search.
*/
const FILTER_ALL = 3;
/**
* @var Communicator The communicator for this client.
*/
protected $com;
/**
* @var int The number of currently pending requests.
*/
protected $pendingRequestsCount = 0;
/**
* @var array An array of responses that have not yet been extracted or
* passed to a callback. Key is the tag of the request, and the value
* is an array of associated responses.
*/
protected $responseBuffer = array();
/**
* @var array An array of callbacks to be executed as responses come.
* Key is the tag of the request, and the value is the callback for it.
*/
protected $callbacks = array();
/**
* @var Registry A registry for the operations. Particularly helpful at
* persistent connections.
*/
protected $registry = null;
/**
* @var bool Whether to stream future responses.
*/
private $_streamingResponses = false;
/**
* Creates a new instance of a RouterOS API client.
*
* Creates a new instance of a RouterOS API client with the specified
* settings.
*
* @param string $host Hostname (IP or domain) of the RouterOS server.
* @param string $username The RouterOS username.
* @param string $password The RouterOS password.
* @param int|null $port The port on which the RouterOS server provides
* the API service. You can also specify NULL, in which case the port
* will automatically be chosen between 8728 and 8729, depending on the
* value of $crypto.
* @param bool $persist Whether or not the connection should be a
* persistent one.
* @param float $timeout The timeout for the connection.
* @param string $crypto The encryption for this connection. Must be one
* of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off
* by default. RouterOS currently supports only TLS, but the setting is
* provided in this fashion for forward compatibility's sake. And for
* the sake of simplicity, if you specify an encryption, don't specify a
* context and your default context uses the value "DEFAULT" for
* ciphers, "ADH" will be automatically added to the list of ciphers.
* @param resource $context A context for the socket.
*
* @see sendSync()
* @see sendAsync()
*/
public function __construct(
$host,
$username,
$password = '',
$port = 8728,
$persist = false,
$timeout = null,
$crypto = N::CRYPTO_OFF,
$context = null
) {
$this->com = new Communicator(
$host,
$port,
$persist,
$timeout,
$username . '/' . $password,
$crypto,
$context
);
$timeout = null == $timeout
? ini_get('default_socket_timeout')
: (int) $timeout;
//Login the user if necessary
if ((!$persist
|| !($old = $this->com->getTransmitter()->lock(S::DIRECTION_ALL)))
&& $this->com->getTransmitter()->isFresh()
) {
if (!static::login($this->com, $username, $password, $timeout)) {
$this->com->close();
throw new DataFlowException(
'Invalid username or password supplied.',
DataFlowException::CODE_INVALID_CREDENTIALS
);
}
}
if (isset($old)) {
$this->com->getTransmitter()->lock($old, true);
}
if ($persist) {
$this->registry = new Registry("{$host}:{$port}/{$username}");
}
}
/**
* A shorthand gateway.
*
* This is a magic PHP method that allows you to call the object as a
* function. Depending on the argument given, one of the other functions in
* the class is invoked and its returned value is returned by this function.
*
* @param mixed $arg Value can be either a {@link Request} to send, which
* would be sent asynchoniously if it has a tag, and synchroniously if
* not, a number to loop with or NULL to complete all pending requests.
* Any other value is converted to string and treated as the tag of a
* request to complete.
*
* @return mixed Whatever the long form function would have returned.
*/
public function __invoke($arg = null)
{
if (is_int($arg) || is_double($arg)) {
return $this->loop($arg);
} elseif ($arg instanceof Request) {
return '' == $arg->getTag() ? $this->sendSync($arg)
: $this->sendAsync($arg);
} elseif (null === $arg) {
return $this->completeRequest();
}
return $this->completeRequest((string) $arg);
}
/**
* Login to a RouterOS connection.
*
* @param Communicator $com The communicator to attempt to login to.
* @param string $username The RouterOS username.
* @param string $password The RouterOS password.
* @param int|null $timeout The time to wait for each response. NULL
* waits indefinetly.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function login(
Communicator $com,
$username,
$password = '',
$timeout = null
) {
if (null !== ($remoteCharset = $com->getCharset($com::CHARSET_REMOTE))
&& null !== ($localCharset = $com->getCharset($com::CHARSET_LOCAL))
) {
$password = iconv(
$localCharset,
$remoteCharset . '//IGNORE//TRANSLIT',
$password
);
}
$old = null;
try {
if ($com->getTransmitter()->isPersistent()) {
$old = $com->getTransmitter()->lock(S::DIRECTION_ALL);
$result = self::_login($com, $username, $password, $timeout);
$com->getTransmitter()->lock($old, true);
return $result;
}
return self::_login($com, $username, $password, $timeout);
} catch (E $e) {
if ($com->getTransmitter()->isPersistent() && null !== $old) {
$com->getTransmitter()->lock($old, true);
}
throw ($e instanceof NotSupportedException
|| $e instanceof UnexpectedValueException
|| !$com->getTransmitter()->isDataAwaiting()) ? new SocketException(
'This is not a compatible RouterOS service',
SocketException::CODE_SERVICE_INCOMPATIBLE,
$e
) : $e;
}
}
/**
* Login to a RouterOS connection.
*
* This is the actual login procedure, applied regardless of persistence and
* charset settings.
*
* @param Communicator $com The communicator to attempt to login to.
* @param string $username The RouterOS username.
* @param string $password The RouterOS password. Potentially parsed
* already by iconv.
* @param int|null $timeout The time to wait for each response. NULL
* waits indefinetly.
*
* @return bool TRUE on success, FALSE on failure.
*/
private static function _login(
Communicator $com,
$username,
$password = '',
$timeout = null
) {
$request = new Request('/login');
$request->send($com);
$response = new Response($com, false, $timeout);
$request->setArgument('name', $username);
$request->setArgument(
'response',
'00' . md5(
chr(0) . $password
. pack('H*', $response->getProperty('ret'))
)
);
$request->send($com);
$response = new Response($com, false, $timeout);
return $response->getType() === Response::TYPE_FINAL
&& null === $response->getProperty('ret');
}
/**
* Sets the charset(s) for this connection.
*
* Sets the charset(s) for this connection. The specified charset(s) will be
* used for all future requests and responses. When sending,
* {@link Communicator::CHARSET_LOCAL} is converted to
* {@link Communicator::CHARSET_REMOTE}, and when receiving,
* {@link Communicator::CHARSET_REMOTE} is converted to
* {@link Communicator::CHARSET_LOCAL}. Setting NULL to either charset will
* disable charset convertion, and data will be both sent and received "as
* is".
*
* @param mixed $charset The charset to set. If $charsetType is
* {@link Communicator::CHARSET_ALL}, you can supply either a string to
* use for all charsets, or an array with the charset types as keys, and
* the charsets as values.
* @param int $charsetType Which charset to set. Valid values are the
* Communicator::CHARSET_* constants. Any other value is treated as
* {@link Communicator::CHARSET_ALL}.
*
* @return string|array The old charset. If $charsetType is
* {@link Communicator::CHARSET_ALL}, the old values will be returned as
* an array with the types as keys, and charsets as values.
* @see Communicator::setDefaultCharset()
*/
public function setCharset(
$charset,
$charsetType = Communicator::CHARSET_ALL
) {
return $this->com->setCharset($charset, $charsetType);
}
/**
* Gets the charset(s) for this connection.
*
* @param int $charsetType Which charset to get. Valid values are the
* Communicator::CHARSET_* constants. Any other value is treated as
* {@link Communicator::CHARSET_ALL}.
*
* @return string|array The current charset. If $charsetType is
* {@link Communicator::CHARSET_ALL}, the current values will be
* returned as an array with the types as keys, and charsets as values.
* @see setCharset()
*/
public function getCharset($charsetType)
{
return $this->com->getCharset($charsetType);
}
/**
* Sends a request and waits for responses.
*
* @param Request $request The request to send.
* @param callback $callback Optional. A function that is to be executed
* when new responses for this request are available. The callback takes
* two parameters. The {@link Response} object as the first, and the
* {@link Client} object as the second one. If the function returns
* TRUE, the request is canceled. Note that the callback may be executed
* one last time after that with a response that notifies about the
* canceling.
*
* @return $this The client object.
* @see completeRequest()
* @see loop()
* @see cancelRequest()
*/
public function sendAsync(Request $request, $callback = null)
{
//Error checking
$tag = $request->getTag();
if ('' == $tag) {
throw new DataFlowException(
'Asynchonous commands must have a tag.',
DataFlowException::CODE_TAG_REQUIRED
);
}
if ($this->isRequestActive($tag)) {
throw new DataFlowException(
'There must not be multiple active requests sharing a tag.',
DataFlowException::CODE_TAG_UNIQUE
);
}
if (null !== $callback && !is_callable($callback, true)) {
throw new UnexpectedValueException(
'Invalid callback provided.',
UnexpectedValueException::CODE_CALLBACK_INVALID
);
}
$this->send($request);
if (null === $callback) {
//Register the request at the buffer
$this->responseBuffer[$tag] = array();
} else {
//Prepare the callback
$this->callbacks[$tag] = $callback;
}
return $this;
}
/**
* Checks if a request is active.
*
* Checks if a request is active. A request is considered active if it's a
* pending request and/or has responses that are not yet extracted.
*
* @param string $tag The tag of the request to look for.
* @param int $filter One of the FILTER_* consntants. Limits the search
* to the specified places.
*
* @return bool TRUE if the request is active, FALSE otherwise.
* @see getPendingRequestsCount()
* @see completeRequest()
*/
public function isRequestActive($tag, $filter = self::FILTER_ALL)
{
$result = 0;
if ($filter & self::FILTER_CALLBACK) {
$result |= (int) array_key_exists($tag, $this->callbacks);
}
if ($filter & self::FILTER_BUFFER) {
$result |= (int) array_key_exists($tag, $this->responseBuffer);
}
return 0 !== $result;
}
/**
* Sends a request and gets the full response.
*
* @param Request $request The request to send.
*
* @return ResponseCollection The received responses as a collection.
* @see sendAsync()
* @see close()
*/
public function sendSync(Request $request)
{
$tag = $request->getTag();
if ('' == $tag) {
$this->send($request);
} else {
$this->sendAsync($request);
}
return $this->completeRequest($tag);
}
/**
* Completes a specified request.
*
* Starts an event loop for the RouterOS callbacks and finishes when a
* specified request is completed.
*
* @param string $tag The tag of the request to complete. Setting NULL
* completes all requests.
*
* @return ResponseCollection A collection of {@link Response} objects that
* haven't been passed to a callback function or previously extracted
* with {@link static::extractNewResponses()}. Returns an empty
* collection when $tag is set to NULL (responses can still be
* extracted).
*/
public function completeRequest($tag = null)
{
$hasNoTag = '' == $tag;
$result = $hasNoTag ? array()
: $this->extractNewResponses($tag)->toArray();
while ((!$hasNoTag && $this->isRequestActive($tag))
|| ($hasNoTag && 0 !== $this->getPendingRequestsCount())
) {
$newReply = $this->dispatchNextResponse(null);
if ($newReply->getTag() === $tag) {
if ($hasNoTag) {
$result[] = $newReply;
}
if ($newReply->getType() === Response::TYPE_FINAL) {
if (!$hasNoTag) {
$result = array_merge(
$result,
$this->isRequestActive($tag)
? $this->extractNewResponses($tag)->toArray()
: array()
);
}
break;
}
}
}
return new ResponseCollection($result);
}
/**
* Extracts responses for a request.
*
* Gets all new responses for a request that haven't been passed to a
* callback and clears the buffer from them.
*
* @param string $tag The tag of the request to extract new responses for.
* Specifying NULL with extract new responses for all requests.
*
* @return ResponseCollection A collection of {@link Response} objects for
* the specified request.
* @see loop()
*/
public function extractNewResponses($tag = null)
{
if (null === $tag) {
$result = array();
foreach (array_keys($this->responseBuffer) as $tag) {
$result = array_merge(
$result,
$this->extractNewResponses($tag)->toArray()
);
}
return new ResponseCollection($result);
} elseif ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
return new ResponseCollection(array());
} elseif ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
$result = $this->responseBuffer[$tag];
if (!empty($result)) {
if (end($result)->getType() === Response::TYPE_FINAL) {
unset($this->responseBuffer[$tag]);
} else {
$this->responseBuffer[$tag] = array();
}
}
return new ResponseCollection($result);
} else {
throw new DataFlowException(
'No such request, or the request has already finished.',
DataFlowException::CODE_UNKNOWN_REQUEST
);
}
}
/**
* Starts an event loop for the RouterOS callbacks.
*
* Starts an event loop for the RouterOS callbacks and finishes when there
* are no more pending requests or when a specified timeout has passed
* (whichever comes first).
*
* @param int $sTimeout Timeout for the loop. If NULL, there is no time
* limit.
* @param int $usTimeout Microseconds to add to the time limit.
*
* @return bool TRUE when there are any more pending requests, FALSE
* otherwise.
* @see extractNewResponses()
* @see getPendingRequestsCount()
*/
public function loop($sTimeout = null, $usTimeout = 0)
{
try {
if (null === $sTimeout) {
while ($this->getPendingRequestsCount() !== 0) {
$this->dispatchNextResponse(null);
}
} else {
list($usStart, $sStart) = explode(' ', microtime());
while ($this->getPendingRequestsCount() !== 0
&& ($sTimeout >= 0 || $usTimeout >= 0)
) {
$this->dispatchNextResponse($sTimeout, $usTimeout);
list($usEnd, $sEnd) = explode(' ', microtime());
$sTimeout -= $sEnd - $sStart;
$usTimeout -= $usEnd - $usStart;
if ($usTimeout <= 0) {
if ($sTimeout > 0) {
$usTimeout = 1000000 + $usTimeout;
$sTimeout--;
}
}
$sStart = $sEnd;
$usStart = $usEnd;
}
}
} catch (SocketException $e) {
if ($e->getCode() !== SocketException::CODE_NO_DATA) {
// @codeCoverageIgnoreStart
// It's impossible to reliably cause any other SocketException.
// This line is only here in case the unthinkable happens:
// The connection terminates just after it was supposedly
// about to send back some data.
throw $e;
// @codeCoverageIgnoreEnd
}
}
return $this->getPendingRequestsCount() !== 0;
}
/**
* Gets the number of pending requests.
*
* @return int The number of pending requests.
* @see isRequestActive()
*/
public function getPendingRequestsCount()
{
return $this->pendingRequestsCount;
}
/**
* Cancels a request.
*
* Cancels an active request. Using this function in favor of a plain call
* to the "/cancel" command is highly reccomended, as it also updates the
* counter of pending requests properly. Note that canceling a request also
* removes any responses for it that were not previously extracted with
* {@link static::extractNewResponses()}.
*
* @param string $tag Tag of the request to cancel. Setting NULL will cancel
* all requests.
*
* @return $this The client object.
* @see sendAsync()
* @see close()
*/
public function cancelRequest($tag = null)
{
$cancelRequest = new Request('/cancel');
$hasTag = !('' == $tag);
$hasReg = null !== $this->registry;
if ($hasReg && !$hasTag) {
$tags = array_merge(
array_keys($this->responseBuffer),
array_keys($this->callbacks)
);
$this->registry->setTaglessMode(true);
foreach ($tags as $t) {
$cancelRequest->setArgument(
'tag',
$this->registry->getOwnershipTag() . $t
);
$this->sendSync($cancelRequest);
}
$this->registry->setTaglessMode(false);
} else {
if ($hasTag) {
if ($this->isRequestActive($tag)) {
if ($hasReg) {
$this->registry->setTaglessMode(true);
$cancelRequest->setArgument(
'tag',
$this->registry->getOwnershipTag() . $tag
);
} else {
$cancelRequest->setArgument('tag', $tag);
}
} else {
throw new DataFlowException(
'No such request. Canceling aborted.',
DataFlowException::CODE_CANCEL_FAIL
);
}
}
$this->sendSync($cancelRequest);
if ($hasReg) {
$this->registry->setTaglessMode(false);
}
}
if ($hasTag) {
if ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
$this->responseBuffer[$tag] = $this->completeRequest($tag);
} else {
$this->completeRequest($tag);
}
} else {
$this->loop();
}
return $this;
}
/**
* Sets response streaming setting.
*
* Sets whether future responses are streamed. If responses are streamed,
* the argument values are returned as streams instead of strings. This is
* particularly useful if you expect a response that may contain one or more
* very large words.
*
* @param bool $streamingResponses Whether to stream future responses.
*
* @return bool The previous value of the setting.
* @see isStreamingResponses()
*/
public function setStreamingResponses($streamingResponses)
{
$oldValue = $this->_streamingResponses;
$this->_streamingResponses = (bool) $streamingResponses;
return $oldValue;
}
/**
* Gets response streaming setting.
*
* Gets whether future responses are streamed.
*
* @return bool The value of the setting.
* @see setStreamingResponses()
*/
public function isStreamingResponses()
{
return $this->_streamingResponses;
}
/**
* Closes the opened connection, even if it is a persistent one.
*
* Closes the opened connection, even if it is a persistent one. Note that
* {@link static::extractNewResponses()} can still be used to extract
* responses collected prior to the closing.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function close()
{
$result = true;
/*
* The check below is done because for some unknown reason
* (either a PHP or a RouterOS bug) calling "/quit" on an encrypted
* connection makes one end hang.
*
* Since encrypted connections only appeared in RouterOS 6.1, and
* the "/quit" call is needed for all <6.0 versions, problems due
* to its absence should be limited to some earlier 6.* versions
* on some RouterBOARD devices.
*/
if ($this->com->getTransmitter()->getCrypto() === N::CRYPTO_OFF) {
if (null !== $this->registry) {
$this->registry->setTaglessMode(true);
}
try {
$response = $this->sendSync(new Request('/quit'));
$result = $response[0]->getType() === Response::TYPE_FATAL;
} catch (SocketException $e) {
$result
= $e->getCode() === SocketException::CODE_REQUEST_SEND_FAIL;
} catch (E $e) {
//Ignore unknown errors.
}
if (null !== $this->registry) {
$this->registry->setTaglessMode(false);
}
}
$result = $result && $this->com->close();
$this->callbacks = array();
$this->pendingRequestsCount = 0;
return $result;
}
/**
* Closes the connection, unless it's a persistent one.
*/
public function __destruct()
{
if ($this->com->getTransmitter()->isPersistent()) {
if (0 !== $this->pendingRequestsCount) {
$this->cancelRequest();
}
} else {
$this->close();
}
}
/**
* Sends a request to RouterOS.
*
* @param Request $request The request to send.
*
* @return $this The client object.
* @see sendSync()
* @see sendAsync()
*/
protected function send(Request $request)
{
$request->send($this->com, $this->registry);
$this->pendingRequestsCount++;
return $this;
}
/**
* Dispatches the next response in queue.
*
* Dispatches the next response in queue, i.e. it executes the associated
* callback if there is one, or places the response in the response buffer.
*
* @param int $sTimeout If a response is not immediatly available, wait
* this many seconds. If NULL, wait indefinetly.
* @param int $usTimeout Microseconds to add to the waiting time.
*
* @throws SocketException When there's no response within the time limit.
* @return Response The dispatched response.
*/
protected function dispatchNextResponse($sTimeout = 0, $usTimeout = 0)
{
$response = new Response(
$this->com,
$this->_streamingResponses,
$sTimeout,
$usTimeout,
$this->registry
);
if ($response->getType() === Response::TYPE_FATAL) {
$this->pendingRequestsCount = 0;
$this->com->close();
return $response;
}
$tag = $response->getTag();
$isLastForRequest = $response->getType() === Response::TYPE_FINAL;
if ($isLastForRequest) {
$this->pendingRequestsCount--;
}
if ('' != $tag) {
if ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
if ($this->callbacks[$tag]($response, $this)) {
$this->cancelRequest($tag);
} elseif ($isLastForRequest) {
unset($this->callbacks[$tag]);
}
} else {
$this->responseBuffer[$tag][] = $response;
}
}
return $response;
}
}

View File

@ -0,0 +1,671 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Using transmitters.
*/
use PEAR2\Net\Transmitter as T;
/**
* A RouterOS communicator.
*
* Implementation of the RouterOS API protocol. Unlike the other classes in this
* package, this class doesn't provide any conviniences beyond the low level
* implementation details (automatic word length encoding/decoding, charset
* translation and data integrity), and because of that, its direct usage is
* strongly discouraged.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
* @see Client
*/
class Communicator
{
/**
* Used when getting/setting all (default) charsets.
*/
const CHARSET_ALL = -1;
/**
* Used when getting/setting the (default) remote charset.
*
* The remote charset is the charset in which RouterOS stores its data.
* If you want to keep compatibility with your Winbox, this charset should
* match the default charset from your Windows' regional settings.
*/
const CHARSET_REMOTE = 0;
/**
* Used when getting/setting the (default) local charset.
*
* The local charset is the charset in which the data from RouterOS will be
* returned as. This charset should match the charset of the place the data
* will eventually be written to.
*/
const CHARSET_LOCAL = 1;
/**
* @var array An array with the default charset types as keys, and the
* default charsets as values.
*/
protected static $defaultCharsets = array(
self::CHARSET_REMOTE => null,
self::CHARSET_LOCAL => null
);
/**
* @var array An array with the current charset types as keys, and the
* current charsets as values.
*/
protected $charsets = array();
/**
* @var T\TcpClient The transmitter for the connection.
*/
protected $trans;
/**
* Creates a new connection with the specified options.
*
* @param string $host Hostname (IP or domain) of the RouterOS server.
* @param int|null $port The port on which the RouterOS server provides
* the API service. You can also specify NULL, in which case the port
* will automatically be chosen between 8728 and 8729, depending on the
* value of $crypto.
* @param bool $persist Whether or not the connection should be a
* persistent one.
* @param float $timeout The timeout for the connection.
* @param string $key A string that uniquely identifies the
* connection.
* @param string $crypto The encryption for this connection. Must be one
* of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off
* by default. RouterOS currently supports only TLS, but the setting is
* provided in this fashion for forward compatibility's sake. And for
* the sake of simplicity, if you specify an encryption, don't specify a
* context and your default context uses the value "DEFAULT" for
* ciphers, "ADH" will be automatically added to the list of ciphers.
* @param resource $context A context for the socket.
*
* @see sendWord()
*/
public function __construct(
$host,
$port = 8728,
$persist = false,
$timeout = null,
$key = '',
$crypto = T\NetworkStream::CRYPTO_OFF,
$context = null
) {
$isUnencrypted = T\NetworkStream::CRYPTO_OFF === $crypto;
if (($context === null) && !$isUnencrypted) {
$context = stream_context_get_default();
$opts = stream_context_get_options($context);
if (!isset($opts['ssl']['ciphers'])
|| 'DEFAULT' === $opts['ssl']['ciphers']
) {
stream_context_set_option($context, 'ssl', 'ciphers', 'ADH');
}
}
// @codeCoverageIgnoreStart
// The $port is customizable in testing.
if (null === $port) {
$port = $isUnencrypted ? 8728 : 8729;
}
// @codeCoverageIgnoreEnd
try {
$this->trans = new T\TcpClient(
$host,
$port,
$persist,
$timeout,
$key,
$crypto,
$context
);
} catch (T\Exception $e) {
throw new SocketException(
'Error connecting to RouterOS',
SocketException::CODE_CONNECTION_FAIL,
$e
);
}
$this->setCharset(
self::getDefaultCharset(self::CHARSET_ALL),
self::CHARSET_ALL
);
}
/**
* A shorthand gateway.
*
* This is a magic PHP method that allows you to call the object as a
* function. Depending on the argument given, one of the other functions in
* the class is invoked and its returned value is returned by this function.
*
* @param string $string A string of the word to send, or NULL to get the
* next word as a string.
*
* @return int|string If a string is provided, returns the number of bytes
* sent, otherwise retuns the next word as a string.
*/
public function __invoke($string = null)
{
return null === $string ? $this->getNextWord()
: $this->sendWord($string);
}
/**
* Checks whether a variable is a seekable stream resource.
*
* @param mixed $var The value to check.
*
* @return bool TRUE if $var is a seekable stream, FALSE otherwise.
*/
public static function isSeekableStream($var)
{
if (T\Stream::isStream($var)) {
$meta = stream_get_meta_data($var);
return $meta['seekable'];
}
return false;
}
/**
* Uses iconv to convert a stream from one charset to another.
*
* @param string $inCharset The charset of the stream.
* @param string $outCharset The desired resulting charset.
* @param resource $stream The stream to convert. The stream is assumed
* to be seekable, and is read from its current position to its end,
* after which, it is seeked back to its starting position.
*
* @return resource A new stream that uses the $out_charset. The stream is a
* subset from the original stream, from its current position to its
* end, seeked at its start.
*/
public static function iconvStream($inCharset, $outCharset, $stream)
{
$bytes = 0;
$result = fopen('php://temp', 'r+b');
$iconvFilter = stream_filter_append(
$result,
'convert.iconv.' . $inCharset . '.' . $outCharset,
STREAM_FILTER_WRITE
);
flock($stream, LOCK_SH);
while (!feof($stream)) {
$bytes += stream_copy_to_stream($stream, $result, 0xFFFFF);
}
fseek($stream, -$bytes, SEEK_CUR);
flock($stream, LOCK_UN);
stream_filter_remove($iconvFilter);
rewind($result);
return $result;
}
/**
* Sets the default charset(s) for new connections.
*
* @param mixed $charset The charset to set. If $charsetType is
* {@link self::CHARSET_ALL}, you can supply either a string to use for
* all charsets, or an array with the charset types as keys, and the
* charsets as values.
* @param int $charsetType Which charset to set. Valid values are the
* CHARSET_* constants. Any other value is treated as
* {@link self::CHARSET_ALL}.
*
* @return string|array The old charset. If $charsetType is
* {@link self::CHARSET_ALL}, the old values will be returned as an
* array with the types as keys, and charsets as values.
* @see setCharset()
*/
public static function setDefaultCharset(
$charset,
$charsetType = self::CHARSET_ALL
) {
if (array_key_exists($charsetType, self::$defaultCharsets)) {
$oldCharset = self::$defaultCharsets[$charsetType];
self::$defaultCharsets[$charsetType] = $charset;
return $oldCharset;
} else {
$oldCharsets = self::$defaultCharsets;
self::$defaultCharsets = is_array($charset) ? $charset : array_fill(
0,
count(self::$defaultCharsets),
$charset
);
return $oldCharsets;
}
}
/**
* Gets the default charset(s).
*
* @param int $charsetType Which charset to get. Valid values are the
* CHARSET_* constants. Any other value is treated as
* {@link self::CHARSET_ALL}.
*
* @return string|array The current charset. If $charsetType is
* {@link self::CHARSET_ALL}, the current values will be returned as an
* array with the types as keys, and charsets as values.
* @see setDefaultCharset()
*/
public static function getDefaultCharset($charsetType)
{
return array_key_exists($charsetType, self::$defaultCharsets)
? self::$defaultCharsets[$charsetType] : self::$defaultCharsets;
}
/**
* Gets the length of a seekable stream.
*
* Gets the length of a seekable stream.
*
* @param resource $stream The stream to check. The stream is assumed to be
* seekable.
*
* @return double The number of bytes in the stream between its current
* position and its end.
*/
public static function seekableStreamLength($stream)
{
$streamPosition = (double) sprintf('%u', ftell($stream));
fseek($stream, 0, SEEK_END);
$streamLength = ((double) sprintf('%u', ftell($stream)))
- $streamPosition;
fseek($stream, $streamPosition, SEEK_SET);
return $streamLength;
}
/**
* Sets the charset(s) for this connection.
*
* Sets the charset(s) for this connection. The specified charset(s) will be
* used for all future words. When sending, {@link self::CHARSET_LOCAL} is
* converted to {@link self::CHARSET_REMOTE}, and when receiving,
* {@link self::CHARSET_REMOTE} is converted to {@link self::CHARSET_LOCAL}.
* Setting NULL to either charset will disable charset convertion, and data
* will be both sent and received "as is".
*
* @param mixed $charset The charset to set. If $charsetType is
* {@link self::CHARSET_ALL}, you can supply either a string to use for
* all charsets, or an array with the charset types as keys, and the
* charsets as values.
* @param int $charsetType Which charset to set. Valid values are the
* CHARSET_* constants. Any other value is treated as
* {@link self::CHARSET_ALL}.
*
* @return string|array The old charset. If $charsetType is
* {@link self::CHARSET_ALL}, the old values will be returned as an
* array with the types as keys, and charsets as values.
* @see setDefaultCharset()
*/
public function setCharset($charset, $charsetType = self::CHARSET_ALL)
{
if (array_key_exists($charsetType, $this->charsets)) {
$oldCharset = $this->charsets[$charsetType];
$this->charsets[$charsetType] = $charset;
return $oldCharset;
} else {
$oldCharsets = $this->charsets;
$this->charsets = is_array($charset) ? $charset : array_fill(
0,
count($this->charsets),
$charset
);
return $oldCharsets;
}
}
/**
* Gets the charset(s) for this connection.
*
* @param int $charsetType Which charset to get. Valid values are the
* CHARSET_* constants. Any other value is treated as
* {@link self::CHARSET_ALL}.
*
* @return string|array The current charset. If $charsetType is
* {@link self::CHARSET_ALL}, the current values will be returned as an
* array with the types as keys, and charsets as values.
* @see getDefaultCharset()
* @see setCharset()
*/
public function getCharset($charsetType)
{
return array_key_exists($charsetType, $this->charsets)
? $this->charsets[$charsetType] : $this->charsets;
}
/**
* Gets the transmitter for this connection.
*
* @return T\TcpClient The transmitter for this connection.
*/
public function getTransmitter()
{
return $this->trans;
}
/**
* Sends a word.
*
* Sends a word and automatically encodes its length when doing so.
*
* @param string $word The word to send.
*
* @return int The number of bytes sent.
* @see sendWordFromStream()
* @see getNextWord()
*/
public function sendWord($word)
{
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
) {
$word = iconv(
$localCharset,
$remoteCharset . '//IGNORE//TRANSLIT',
$word
);
}
$length = strlen($word);
static::verifyLengthSupport($length);
if ($this->trans->isPersistent()) {
$old = $this->trans->lock(T\Stream::DIRECTION_SEND);
$bytes = $this->trans->send(self::encodeLength($length) . $word);
$this->trans->lock($old, true);
return $bytes;
}
return $this->trans->send(self::encodeLength($length) . $word);
}
/**
* Sends a word based on a stream.
*
* Sends a word based on a stream and automatically encodes its length when
* doing so. The stream is read from its current position to its end, and
* then returned to its current position. Because of those operations, the
* supplied stream must be seekable.
*
* @param string $prefix A string to prepend before the stream contents.
* @param resource $stream The seekable stream to send.
*
* @return int The number of bytes sent.
* @see sendWord()
*/
public function sendWordFromStream($prefix, $stream)
{
if (!self::isSeekableStream($stream)) {
throw new InvalidArgumentException(
'The stream must be seekable.',
InvalidArgumentException::CODE_SEEKABLE_REQUIRED
);
}
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
) {
$prefix = iconv(
$localCharset,
$remoteCharset . '//IGNORE//TRANSLIT',
$prefix
);
$stream = self::iconvStream(
$localCharset,
$remoteCharset . '//IGNORE//TRANSLIT',
$stream
);
}
flock($stream, LOCK_SH);
$totalLength = strlen($prefix) + self::seekableStreamLength($stream);
static::verifyLengthSupport($totalLength);
$bytes = $this->trans->send(self::encodeLength($totalLength) . $prefix);
$bytes += $this->trans->send($stream);
flock($stream, LOCK_UN);
return $bytes;
}
/**
* Verifies that the length is supported.
*
* Verifies if the specified length is supported by the API. Throws a
* {@link LengthException} if that's not the case. Currently, RouterOS
* supports words up to 0xFFFFFFFF in length, so that's the only check
* performed.
*
* @param int $length The length to verify.
*
* @return void
*/
protected static function verifyLengthSupport($length)
{
if ($length > 0xFFFFFFFF) {
throw new LengthException(
'Words with length above 0xFFFFFFFF are not supported.',
LengthException::CODE_UNSUPPORTED,
null,
$length
);
}
}
/**
* Encodes the length as requred by the RouterOS API.
*
* @param int $length The length to encode.
*
* @return string The encoded length.
*/
public static function encodeLength($length)
{
if ($length < 0) {
throw new LengthException(
'Length must not be negative.',
LengthException::CODE_INVALID,
null,
$length
);
} elseif ($length < 0x80) {
return chr($length);
} elseif ($length < 0x4000) {
return pack('n', $length |= 0x8000);
} elseif ($length < 0x200000) {
$length |= 0xC00000;
return pack('n', $length >> 8) . chr($length & 0xFF);
} elseif ($length < 0x10000000) {
return pack('N', $length |= 0xE0000000);
} elseif ($length <= 0xFFFFFFFF) {
return chr(0xF0) . pack('N', $length);
} elseif ($length <= 0x7FFFFFFFF) {
$length = 'f' . base_convert($length, 10, 16);
return chr(hexdec(substr($length, 0, 2))) .
pack('N', hexdec(substr($length, 2)));
}
throw new LengthException(
'Length must not be above 0x7FFFFFFFF.',
LengthException::CODE_BEYOND_SHEME,
null,
$length
);
}
/**
* Get the next word in queue as a string.
*
* Get the next word in queue as a string, after automatically decoding its
* length.
*
* @return string The word.
* @see close()
*/
public function getNextWord()
{
if ($this->trans->isPersistent()) {
$old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE);
$word = $this->trans->receive(
self::decodeLength($this->trans),
'word'
);
$this->trans->lock($old, true);
} else {
$word = $this->trans->receive(
self::decodeLength($this->trans),
'word'
);
}
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
) {
$word = iconv(
$remoteCharset,
$localCharset . '//IGNORE//TRANSLIT',
$word
);
}
return $word;
}
/**
* Get the next word in queue as a stream.
*
* Get the next word in queue as a stream, after automatically decoding its
* length.
*
* @return resource The word, as a stream.
* @see close()
*/
public function getNextWordAsStream()
{
$filters = new T\FilterCollection();
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
) {
$filters->append(
'convert.iconv.' .
$remoteCharset . '.' . $localCharset . '//IGNORE//TRANSLIT'
);
}
if ($this->trans->isPersistent()) {
$old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE);
$stream = $this->trans->receiveStream(
self::decodeLength($this->trans),
$filters,
'stream word'
);
$this->trans->lock($old, true);
} else {
$stream = $this->trans->receiveStream(
self::decodeLength($this->trans),
$filters,
'stream word'
);
}
return $stream;
}
/**
* Decodes the lenght of the incoming message.
*
* Decodes the lenght of the incoming message, as specified by the RouterOS
* API.
*
* @param T\Stream $trans The transmitter from which to decode the length of
* the incoming message.
*
* @return int The decoded length.
*/
public static function decodeLength(T\Stream $trans)
{
if ($trans->isPersistent() && $trans instanceof T\TcpClient) {
$old = $trans->lock($trans::DIRECTION_RECEIVE);
$length = self::_decodeLength($trans);
$trans->lock($old, true);
return $length;
}
return self::_decodeLength($trans);
}
/**
* Decodes the lenght of the incoming message.
*
* Decodes the lenght of the incoming message, as specified by the RouterOS
* API.
*
* Difference with the non private function is that this one doesn't perform
* locking if the connection is a persistent one.
*
* @param T\Stream $trans The transmitter from which to decode the length of
* the incoming message.
*
* @return int The decoded length.
*/
private static function _decodeLength(T\Stream $trans)
{
$byte = ord($trans->receive(1, 'initial length byte'));
if ($byte & 0x80) {
if (($byte & 0xC0) === 0x80) {
return (($byte & 077) << 8 ) + ord($trans->receive(1));
} elseif (($byte & 0xE0) === 0xC0) {
$rem = unpack('n~', $trans->receive(2));
return (($byte & 037) << 16 ) + $rem['~'];
} elseif (($byte & 0xF0) === 0xE0) {
$rem = unpack('n~/C~~', $trans->receive(3));
return (($byte & 017) << 24 ) + ($rem['~'] << 8) + $rem['~~'];
} elseif (($byte & 0xF8) === 0xF0) {
$rem = unpack('N~', $trans->receive(4));
return (($byte & 007) * 0x100000000/* '<< 32' or '2^32' */)
+ (double) sprintf('%u', $rem['~']);
}
throw new NotSupportedException(
'Unknown control byte encountered.',
NotSupportedException::CODE_CONTROL_BYTE,
null,
$byte
);
} else {
return $byte;
}
}
/**
* Closes the opened connection, even if it is a persistent one.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function close()
{
return $this->trans->close();
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Base of this class.
*/
use RuntimeException;
/**
* Exception thrown when the request/response cycle goes an unexpected way.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class DataFlowException extends RuntimeException implements Exception
{
const CODE_INVALID_CREDENTIALS = 10000;
const CODE_TAG_REQUIRED = 10500;
const CODE_TAG_UNIQUE = 10501;
const CODE_UNKNOWN_REQUEST = 10900;
const CODE_CANCEL_FAIL = 11200;
}

View File

@ -0,0 +1,34 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Generic exception class of this package.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
interface Exception
{
}

View File

@ -0,0 +1,43 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
use InvalidArgumentException as I;
/**
* Exception thrown when there's something wrong with message arguments.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class InvalidArgumentException extends I implements Exception
{
const CODE_SEEKABLE_REQUIRED = 1100;
const CODE_NAME_INVALID = 20100;
const CODE_ABSOLUTE_REQUIRED = 40200;
const CODE_CMD_UNRESOLVABLE = 40201;
const CODE_CMD_INVALID = 40202;
const CODE_NAME_UNPARSABLE = 41000;
const CODE_VALUE_UNPARSABLE = 41001;
}

View File

@ -0,0 +1,93 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Base of this class.
*/
use LengthException as L;
/**
* Exception thrown when there is a problem with a word's length.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class LengthException extends L implements Exception
{
const CODE_UNSUPPORTED = 1200;
const CODE_INVALID = 1300;
const CODE_BEYOND_SHEME = 1301;
/**
*
* @var mixed The problematic length.
*/
private $_length;
/**
* Creates a new LengthException.
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param \Exception $previous The previous exception used for the exception
* chaining.
* @param number $length The length.
*/
public function __construct(
$message,
$code = 0,
$previous = null,
$length = null
) {
parent::__construct($message, $code, $previous);
$this->_length = $length;
}
/**
* Gets the length.
*
* @return number The length.
*/
public function getLength()
{
return $this->_length;
}
// @codeCoverageIgnoreStart
// String representation is not reliable in testing
/**
* Returns a string representation of the exception.
*
* @return string The exception as a string.
*/
public function __toString()
{
return parent::__toString() . "\nLength:{$this->_length}";
}
// @codeCoverageIgnoreEnd
}

View File

@ -0,0 +1,237 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Implements this interface.
*/
use Countable;
/**
* Implements this interface.
*/
use IteratorAggregate;
/**
* Requred for IteratorAggregate::getIterator() to work properly with foreach.
*/
use ArrayObject;
/**
* Represents a RouterOS message.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
abstract class Message implements IteratorAggregate, Countable
{
/**
* @var array An array with message attributes. Each array key is the the
* name of an attribute, and the correspding array value is the value
* for that attribute.
*/
protected $attributes = array();
/**
* @var string An optional tag to associate the message with.
*/
private $_tag = null;
/**
* A shorthand gateway.
*
* This is a magic PHP method that allows you to call the object as a
* function. Depending on the argument given, one of the other functions in
* the class is invoked and its returned value is returned by this function.
*
* @param string $name The name of an attribute to get the value of, or NULL
* to get the tag.
*
* @return string|resource The value of the specified attribute,
* or the tag if NULL is provided.
*/
public function __invoke($name = null)
{
if (null === $name) {
return $this->getTag();
}
return $this->getAttribute($name);
}
/**
* Sanitizes a name of an attribute (message or query one).
*
* @param mixed $name The name to sanitize.
*
* @return string The sanitized name.
*/
public static function sanitizeAttributeName($name)
{
$name = (string) $name;
if ((empty($name) && $name !== '0')
|| preg_match('/[=\s]/s', $name)
) {
throw new InvalidArgumentException(
'Invalid name of argument supplied.',
InvalidArgumentException::CODE_NAME_INVALID
);
}
return $name;
}
/**
* Sanitizes a value of an attribute (message or query one).
*
* @param mixed $value The value to sanitize.
*
* @return string The sanitized value.
*/
public static function sanitizeAttributeValue($value)
{
if (Communicator::isSeekableStream($value)) {
return $value;
} else {
return (string) $value;
}
}
/**
* Gets the tag that the message is associated with.
*
* @return string The current tag or NULL if there isn't a tag.
* @see setTag()
*/
public function getTag()
{
return $this->_tag;
}
/**
* Sets the tag to associate the request with.
*
* Sets the tag to associate the message with. Setting NULL erases the
* currently set tag.
*
* @param string $tag The tag to set.
*
* @return $this The message object.
* @see getTag()
*/
protected function setTag($tag)
{
$this->_tag = (null === $tag) ? null : (string) $tag;
return $this;
}
/**
* Gets the value of an attribute.
*
* @param string $name The name of the attribute.
*
* @return string|resource|null The value of the specified attribute.
* Returns NULL if such an attribute is not set.
* @see setAttribute()
*/
protected function getAttribute($name)
{
$name = self::sanitizeAttributeName($name);
if (array_key_exists($name, $this->attributes)) {
return $this->attributes[$name];
}
return null;
}
/**
* Gets all arguments in an array.
*
* @return ArrayObject An ArrayObject with the keys being argument names,
* and the array values being argument values.
* @see getArgument()
* @see setArgument()
*/
public function getIterator()
{
return new ArrayObject($this->attributes);
}
/**
* Counts the number of arguments.
*
* @param int $mode The counter mode.
* Either COUNT_NORMAL or COUNT_RECURSIVE.
* When in normal mode, counts the number of arguments.
* When in recursive mode, counts the number of API words
* (including the empty word at the end).
*
* @return int The number of arguments/words.
*/
public function count($mode = COUNT_NORMAL)
{
$result = count($this->attributes);
if ($mode !== COUNT_NORMAL) {
$result += 2/*first+last word*/
+ (int)(null !== $this->getTag());
}
return $result;
}
/**
* Sets an attribute for the message.
*
* @param string $name Name of the attribute.
* @param string|resource|null $value Value of the attribute as a string or
* seekable stream.
* Setting the value to NULL removes an argument of this name.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
*
* @return $this The message object.
* @see getArgument()
*/
protected function setAttribute($name, $value = '')
{
if (null === $value) {
unset($this->attributes[self::sanitizeAttributeName($name)]);
} else {
$this->attributes[self::sanitizeAttributeName($name)]
= self::sanitizeAttributeValue($value);
}
return $this;
}
/**
* Removes all attributes from the message.
*
* @return $this The message object.
*/
protected function removeAllAttributes()
{
$this->attributes = array();
return $this;
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Base of this class.
*/
use Exception as E;
/**
* Exception thrown when encountering something not supported by RouterOS or
* this package.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class NotSupportedException extends E implements Exception
{
const CODE_CONTROL_BYTE = 1601;
/**
* @var mixed The unsuppported value.
*/
private $_value;
/**
* Creates a new NotSupportedException.
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param \Exception $previous The previous exception used for the exception
* chaining.
* @param mixed $value The unsupported value.
*/
public function __construct(
$message,
$code = 0,
$previous = null,
$value = null
) {
parent::__construct($message, $code, $previous);
$this->_value = $value;
}
/**
* Gets the unsupported value.
*
* @return mixed The unsupported value.
*/
public function getValue()
{
return $this->_value;
}
// @codeCoverageIgnoreStart
// String representation is not reliable in testing
/**
* Returns a string representation of the exception.
*
* @return string The exception as a string.
*/
public function __toString()
{
return parent::__toString() . "\nValue:{$this->_value}";
}
// @codeCoverageIgnoreEnd
}

View File

@ -0,0 +1,267 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Refers to transmitter direction constants.
*/
use PEAR2\Net\Transmitter as T;
/**
* Represents a query for RouterOS requests.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class Query
{
/**
* Checks if the property exists.
*/
const OP_EX = '';
/**
* Checks if the property does not exist.
*/
const OP_NEX = '-';
/**
* Checks if the property equals a certain value.
*/
const OP_EQ = '=';
/**
* Checks if the property is less than a certain value.
*/
const OP_LT = '<';
/**
* Checks if the property is greather than a certain value.
*/
const OP_GT = '>';
/**
* @var array An array of the words forming the query. Each value is an
* array with the first member being the predicate (operator and name),
* and the second member being the value for the predicate.
*/
protected $words = array();
/**
* This class is not to be instantiated normally, but by static methods
* instead. Use {@link static::where()} to create an instance of it.
*/
private function __construct()
{
}
/**
* Sanitizes the operator of a condition.
*
* @param string $operator The operator to sanitize.
*
* @return string The sanitized operator.
*/
protected static function sanitizeOperator($operator)
{
$operator = (string) $operator;
switch ($operator) {
case Query::OP_EX:
case Query::OP_NEX:
case Query::OP_EQ:
case Query::OP_LT:
case Query::OP_GT:
return $operator;
default:
throw new UnexpectedValueException(
'Unknown operator specified',
UnexpectedValueException::CODE_ACTION_UNKNOWN,
null,
$operator
);
}
}
/**
* Creates a new query with an initial condition.
*
* @param string $name The name of the property to test.
* @param string|resource|null $value Value of the property as a string
* or seekable stream. Not required for existence tests.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
* @param string $operator One of the OP_* constants.
* Describes the operation to perform.
*
* @return static A new query object.
*/
public static function where(
$name,
$value = null,
$operator = self::OP_EX
) {
$query = new static;
return $query->addWhere($name, $value, $operator);
}
/**
* Negates the query.
*
* @return $this The query object.
*/
public function not()
{
$this->words[] = array('#!', null);
return $this;
}
/**
* Adds a condition as an alternative to the query.
*
* @param string $name The name of the property to test.
* @param string|resource|null $value Value of the property as a string
* or seekable stream. Not required for existence tests.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
* @param string $operator One of the OP_* constants.
* Describes the operation to perform.
*
* @return $this The query object.
*/
public function orWhere($name, $value = null, $operator = self::OP_EX)
{
$this->addWhere($name, $value, $operator)->words[] = array('#|', null);
return $this;
}
/**
* Adds a condition in addition to the query.
*
* @param string $name The name of the property to test.
* @param string|resource|null $value Value of the property as a string
* or seekable stream. Not required for existence tests.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
* @param string $operator One of the OP_* constants.
* Describes the operation to perform.
*
* @return $this The query object.
*/
public function andWhere($name, $value = null, $operator = self::OP_EX)
{
$this->addWhere($name, $value, $operator)->words[] = array('#&', null);
return $this;
}
/**
* Sends the query over a communicator.
*
* @param Communicator $com The communicator to send the query over.
*
* @return int The number of bytes sent.
*/
public function send(Communicator $com)
{
if ($com->getTransmitter()->isPersistent()) {
$old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND);
$bytes = $this->_send($com);
$com->getTransmitter()->lock($old, true);
return $bytes;
}
return $this->_send($com);
}
/**
* Sends the query over a communicator.
*
* The only difference with the non private equivalent is that this one does
* not do locking.
*
* @param Communicator $com The communicator to send the query over.
*
* @return int The number of bytes sent.
*/
private function _send(Communicator $com)
{
if (!$com->getTransmitter()->isAcceptingData()) {
throw new SocketException(
'Transmitter is invalid. Sending aborted.',
SocketException::CODE_QUERY_SEND_FAIL
);
}
$bytes = 0;
foreach ($this->words as $queryWord) {
list($predicate, $value) = $queryWord;
$prefix = '?' . $predicate;
if (null === $value) {
$bytes += $com->sendWord($prefix);
} else {
$prefix .= '=';
if (is_string($value)) {
$bytes += $com->sendWord($prefix . $value);
} else {
$bytes += $com->sendWordFromStream($prefix, $value);
}
}
}
return $bytes;
}
/**
* Adds a condition.
*
* @param string $name The name of the property to test.
* @param string|resource|null $value Value of the property as a string
* or seekable stream. Not required for existence tests.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
* @param string $operator One of the ACTION_* constants.
* Describes the operation to perform.
*
* @return $this The query object.
*/
protected function addWhere($name, $value, $operator)
{
$this->words[] = array(
static::sanitizeOperator($operator)
. Message::sanitizeAttributeName($name),
(null === $value ? null : Message::sanitizeAttributeValue($value))
);
return $this;
}
}

View File

@ -0,0 +1,279 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Uses shared memory to keep responses in.
*/
use PEAR2\Cache\SHM;
/**
* A RouterOS registry.
*
* Provides functionality for managing the request/response flow. Particularly
* useful in persistent connections.
*
* Note that this class is not meant to be called directly.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class Registry
{
/**
* @var SHM The storage.
*/
protected $shm;
/**
* @var int ID of request. Populated at first instance in request.
*/
protected static $requestId = -1;
/**
* @var int ID to be given to next instance, after incrementing it.
*/
protected static $instanceIdSeed = -1;
/**
* @var int ID of instance within the request.
*/
protected $instanceId;
/**
* Creates a registry.
*
* @param string $uri An URI to bind the registry to.
*/
public function __construct($uri)
{
$this->shm = SHM::factory(__CLASS__ . ' ' . $uri);
if (-1 === self::$requestId) {
self::$requestId = $this->shm->add('requestId', 0)
? 0 : $this->shm->inc('requestId');
}
$this->instanceId = ++self::$instanceIdSeed;
$this->shm->add('responseBuffer_' . $this->getOwnershipTag(), array());
}
/**
* Parses a tag.
*
* Parses a tag to reveal the ownership part of it, and the original tag.
*
* @param string $tag The tag (as received) to parse.
*
* @return array An array with the first member being the ownership tag, and
* the second one being the original tag.
*/
public static function parseTag($tag)
{
if (null === $tag) {
return array(null, null);
}
$result = explode('__', $tag, 2);
$result[0] .= '__';
if ('' === $result[1]) {
$result[1] = null;
}
return $result;
}
/**
* Checks if this instance is the tagless mode owner.
*
* @return bool TRUE if this instance is the tagless mode owner, FALSE
* otherwise.
*/
public function isTaglessModeOwner()
{
$this->shm->lock('taglessModeOwner');
$result = $this->shm->exists('taglessModeOwner')
&& $this->getOwnershipTag() === $this->shm->get('taglessModeOwner');
$this->shm->unlock('taglessModeOwner');
return $result;
}
/**
* Sets the "tagless mode" setting.
*
* While in tagless mode, this instance will claim owhership of any
* responses without a tag. While not in this mode, any requests without a
* tag will be given to all instances.
*
* Regardless of mode, if the type of the response is
* {@link Response::TYPE_FATAL}, it will be given to all instances.
*
* @param bool $taglessMode TRUE to claim tagless ownership, FALSE to
* release such ownership, if taken.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setTaglessMode($taglessMode)
{
return $taglessMode
? ($this->shm->lock('taglessMode')
&& $this->shm->lock('taglessModeOwner')
&& $this->shm->add('taglessModeOwner', $this->getOwnershipTag())
&& $this->shm->unlock('taglessModeOwner'))
: ($this->isTaglessModeOwner()
&& $this->shm->lock('taglessModeOwner')
&& $this->shm->delete('taglessModeOwner')
&& $this->shm->unlock('taglessModeOwner')
&& $this->shm->unlock('taglessMode'));
}
/**
* Get the ownership tag for this instance.
*
* @return string The ownership tag for this registry instance.
*/
public function getOwnershipTag()
{
return self::$requestId . '_' . $this->instanceId . '__';
}
/**
* Add a response to the registry.
*
* @param Response $response The response to add. The caller of this
* function is responsible for ensuring that the ownership tag and the
* original tag are separated, so that only the original one remains in
* the response.
* @param string $ownershipTag The ownership tag that the response had.
*
* @return bool TRUE if the request was added to its buffer, FALSE if
* this instance owns the response, and therefore doesn't need to add
* the response to its buffer.
*/
public function add(Response $response, $ownershipTag)
{
if ($this->getOwnershipTag() === $ownershipTag
|| ($this->isTaglessModeOwner()
&& $response->getType() !== Response::TYPE_FATAL)
) {
return false;
}
if (null === $ownershipTag) {
$this->shm->lock('taglessModeOwner');
if ($this->shm->exists('taglessModeOwner')
&& $response->getType() !== Response::TYPE_FATAL
) {
$ownershipTag = $this->shm->get('taglessModeOwner');
$this->shm->unlock('taglessModeOwner');
} else {
$this->shm->unlock('taglessModeOwner');
foreach ($this->shm->getIterator(
'/^(responseBuffer\_)/',
true
) as $targetBufferName) {
$this->_add($response, $targetBufferName);
}
return true;
}
}
$this->_add($response, 'responseBuffer_' . $ownershipTag);
return true;
}
/**
* Adds a response to a buffer.
*
* @param Response $response The response to add.
* @param string $targetBufferName The name of the buffer to add the
* response to.
*
* @return void
*/
private function _add(Response $response, $targetBufferName)
{
if ($this->shm->lock($targetBufferName)) {
$targetBuffer = $this->shm->get($targetBufferName);
$targetBuffer[] = $response;
$this->shm->set($targetBufferName, $targetBuffer);
$this->shm->unlock($targetBufferName);
}
}
/**
* Gets the next response from this instance's buffer.
*
* @return Response|null The next response, or NULL if there isn't one.
*/
public function getNextResponse()
{
$response = null;
$targetBufferName = 'responseBuffer_' . $this->getOwnershipTag();
if ($this->shm->exists($targetBufferName)
&& $this->shm->lock($targetBufferName)
) {
$targetBuffer = $this->shm->get($targetBufferName);
if (!empty($targetBuffer)) {
$response = array_shift($targetBuffer);
$this->shm->set($targetBufferName, $targetBuffer);
}
$this->shm->unlock($targetBufferName);
}
return $response;
}
/**
* Closes the registry.
*
* Closes the registry, meaning that all buffers are cleared.
*
* @return void
*/
public function close()
{
self::$requestId = -1;
self::$instanceIdSeed = -1;
$this->shm->clear();
}
/**
* Removes a buffer.
*
* @param string $targetBufferName The buffer to remove.
*
* @return void
*/
private function _close($targetBufferName)
{
if ($this->shm->lock($targetBufferName)) {
$this->shm->delete($targetBufferName);
$this->shm->unlock($targetBufferName);
}
}
/**
* Removes this instance's buffer.
*/
public function __destruct()
{
$this->_close('responseBuffer_' . $this->getOwnershipTag());
}
}

View File

@ -0,0 +1,403 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Refers to transmitter direction constants.
*/
use PEAR2\Net\Transmitter as T;
/**
* Represents a RouterOS request.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class Request extends Message
{
/**
* @var string The command to be executed.
*/
private $_command;
/**
* @var Query A query for the command.
*/
private $_query;
/**
* Creates a request to send to RouterOS.
*
* @param string $command The command to send. Can also contain arguments
* expressed in a shell-like syntax.
* @param Query $query A query to associate with the request.
* @param string $tag The tag for the request.
*
* @see setCommand()
* @see setArgument()
* @see setTag()
* @see setQuery()
*/
public function __construct($command, Query $query = null, $tag = null)
{
if (false !== strpos($command, '=')
&& false !== ($spaceBeforeEquals = strrpos(
strstr($command, '=', true),
' '
))
) {
$this->parseArgumentString(substr($command, $spaceBeforeEquals));
$command = rtrim(substr($command, 0, $spaceBeforeEquals));
}
$this->setCommand($command);
$this->setQuery($query);
$this->setTag($tag);
}
/**
* A shorthand gateway.
*
* This is a magic PHP method that allows you to call the object as a
* function. Depending on the argument given, one of the other functions in
* the class is invoked and its returned value is returned by this function.
*
* @param Query|Communicator|string|null $arg A {@link Query} to associate
* the request with, a {@link Communicator} to send the request over,
* an argument to get the value of, or NULL to get the tag. If a
* second argument is provided, this becomes the name of the argument to
* set the value of, and the second argument is the value to set.
*
* @return string|resource|int|$this Whatever the long form
* function returns.
*/
public function __invoke($arg = null)
{
if (func_num_args() > 1) {
return $this->setArgument(func_get_arg(0), func_get_arg(1));
}
if ($arg instanceof Query) {
return $this->setQuery($arg);
}
if ($arg instanceof Communicator) {
return $this->send($arg);
}
return parent::__invoke($arg);
}
/**
* Sets the command to send to RouterOS.
*
* Sets the command to send to RouterOS. The command can use the API or CLI
* syntax of RouterOS, but either way, it must be absolute (begin with a
* "/") and without arguments.
*
* @param string $command The command to send.
*
* @return $this The request object.
* @see getCommand()
* @see setArgument()
*/
public function setCommand($command)
{
$command = (string) $command;
if (strpos($command, '/') !== 0) {
throw new InvalidArgumentException(
'Commands must be absolute.',
InvalidArgumentException::CODE_ABSOLUTE_REQUIRED
);
}
if (substr_count($command, '/') === 1) {
//Command line syntax convertion
$cmdParts = preg_split('#[\s/]+#sm', $command);
$cmdRes = array($cmdParts[0]);
for ($i = 1, $n = count($cmdParts); $i < $n; $i++) {
if ('..' === $cmdParts[$i]) {
$delIndex = count($cmdRes) - 1;
if ($delIndex < 1) {
throw new InvalidArgumentException(
'Unable to resolve command',
InvalidArgumentException::CODE_CMD_UNRESOLVABLE
);
}
unset($cmdRes[$delIndex]);
$cmdRes = array_values($cmdRes);
} else {
$cmdRes[] = $cmdParts[$i];
}
}
$command = implode('/', $cmdRes);
}
if (!preg_match('#^/\S+$#sm', $command)) {
throw new InvalidArgumentException(
'Invalid command supplied.',
InvalidArgumentException::CODE_CMD_INVALID
);
}
$this->_command = $command;
return $this;
}
/**
* Gets the command that will be send to RouterOS.
*
* Gets the command that will be send to RouterOS in its API syntax.
*
* @return string The command to send.
* @see setCommand()
*/
public function getCommand()
{
return $this->_command;
}
/**
* Sets the query to send with the command.
*
* @param Query $query The query to be set. Setting NULL will remove the
* currently associated query.
*
* @return $this The request object.
* @see getQuery()
*/
public function setQuery(Query $query = null)
{
$this->_query = $query;
return $this;
}
/**
* Gets the currently associated query
*
* @return Query The currently associated query.
* @see setQuery()
*/
public function getQuery()
{
return $this->_query;
}
/**
* Sets the tag to associate the request with.
*
* Sets the tag to associate the request with. Setting NULL erases the
* currently set tag.
*
* @param string $tag The tag to set.
*
* @return $this The request object.
* @see getTag()
*/
public function setTag($tag)
{
return parent::setTag($tag);
}
/**
* Sets an argument for the request.
*
* @param string $name Name of the argument.
* @param string|resource|null $value Value of the argument as a string or
* seekable stream.
* Setting the value to NULL removes an argument of this name.
* If a seekable stream is provided, it is sent from its current
* posistion to its end, and the pointer is seeked back to its current
* position after sending.
* Non seekable streams, as well as all other types, are casted to a
* string.
*
* @return $this The request object.
* @see getArgument()
*/
public function setArgument($name, $value = '')
{
return parent::setAttribute($name, $value);
}
/**
* Gets the value of an argument.
*
* @param string $name The name of the argument.
*
* @return string|resource|null The value of the specified argument.
* Returns NULL if such an argument is not set.
* @see setAttribute()
*/
public function getArgument($name)
{
return parent::getAttribute($name);
}
/**
* Removes all arguments from the request.
*
* @return $this The request object.
*/
public function removeAllArguments()
{
return parent::removeAllAttributes();
}
/**
* Sends a request over a communicator.
*
* @param Communicator $com The communicator to send the request over.
* @param Registry $reg An optional registry to sync the request with.
*
* @return int The number of bytes sent.
* @see Client::sendSync()
* @see Client::sendAsync()
*/
public function send(Communicator $com, Registry $reg = null)
{
if (null !== $reg
&& (null != $this->getTag() || !$reg->isTaglessModeOwner())
) {
$originalTag = $this->getTag();
$this->setTag($reg->getOwnershipTag() . $originalTag);
$bytes = $this->send($com);
$this->setTag($originalTag);
return $bytes;
}
if ($com->getTransmitter()->isPersistent()) {
$old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND);
$bytes = $this->_send($com);
$com->getTransmitter()->lock($old, true);
return $bytes;
}
return $this->_send($com);
}
/**
* Sends a request over a communicator.
*
* The only difference with the non private equivalent is that this one does
* not do locking.
*
* @param Communicator $com The communicator to send the request over.
*
* @return int The number of bytes sent.
* @see Client::sendSync()
* @see Client::sendAsync()
*/
private function _send(Communicator $com)
{
if (!$com->getTransmitter()->isAcceptingData()) {
throw new SocketException(
'Transmitter is invalid. Sending aborted.',
SocketException::CODE_REQUEST_SEND_FAIL
);
}
$bytes = 0;
$bytes += $com->sendWord($this->getCommand());
if (null !== ($tag = $this->getTag())) {
$bytes += $com->sendWord('.tag=' . $tag);
}
foreach ($this->attributes as $name => $value) {
$prefix = '=' . $name . '=';
if (is_string($value)) {
$bytes += $com->sendWord($prefix . $value);
} else {
$bytes += $com->sendWordFromStream($prefix, $value);
}
}
$query = $this->getQuery();
if ($query instanceof Query) {
$bytes += $query->send($com);
}
$bytes += $com->sendWord('');
return $bytes;
}
/**
* Parses the arguments of a command.
*
* @param string $string The argument string to parse.
*
* @return void
*/
protected function parseArgumentString($string)
{
/*
* Grammar:
*
* <arguments> := (<<\s+>>, <argument>)*,
* <argument> := <name>, <value>?
* <name> := <<[^\=\s]+>>
* <value> := "=", (<quoted string> | <unquoted string>)
* <quotedString> := <<">>, <<([^"]|\\"|\\\\)*>>, <<">>
* <unquotedString> := <<\S+>>
*/
$token = '';
$name = null;
while ($string = substr($string, strlen($token))) {
if (null === $name) {
if (preg_match('/^\s+([^\s=]+)/sS', $string, $matches)) {
$token = $matches[0];
$name = $matches[1];
} else {
throw new InvalidArgumentException(
"Parsing of argument name failed near '{$string}'",
InvalidArgumentException::CODE_NAME_UNPARSABLE
);
}
} elseif (preg_match('/^\s/s', $string, $matches)) {
//Empty argument
$token = '';
$this->setArgument($name);
$name = null;
} elseif (preg_match(
'/^="(([^\\\"]|\\\"|\\\\)*)"/sS',
$string,
$matches
)) {
$token = $matches[0];
$this->setArgument(
$name,
str_replace(
array('\\"', '\\\\'),
array('"', '\\'),
$matches[1]
)
);
$name = null;
} elseif (preg_match('/^=(\S+)/sS', $string, $matches)) {
$token = $matches[0];
$this->setArgument($name, $matches[1]);
$name = null;
} else {
throw new InvalidArgumentException(
"Parsing of argument value failed near '{$string}'",
InvalidArgumentException::CODE_VALUE_UNPARSABLE
);
}
}
if (null !== $name && ('' !== ($name = trim($name)))) {
$this->setArgument($name, '');
}
}
}

View File

@ -0,0 +1,335 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Refers to transmitter direction constants.
*/
use PEAR2\Net\Transmitter as T;
/**
* Locks are released upon any exception from anywhere.
*/
use Exception as E;
/**
* Represents a RouterOS response.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class Response extends Message
{
/**
* The last response for a request.
*/
const TYPE_FINAL = '!done';
/**
* A response with data.
*/
const TYPE_DATA = '!re';
/**
* A response signifying error.
*/
const TYPE_ERROR = '!trap';
/**
* A response signifying a fatal error, due to which the connection would be
* terminated.
*/
const TYPE_FATAL = '!fatal';
/**
* @var array An array of unrecognized words in network order.
*/
protected $unrecognizedWords = array();
/**
* @var string The response type.
*/
private $_type;
/**
* Extracts a new response from a communicator.
*
* @param Communicator $com The communicator from which to extract
* the new response.
* @param bool $asStream Whether to populate the argument values
* with streams instead of strings.
* @param int $sTimeout If a response is not immediatly
* available, wait this many seconds. If NULL, wait indefinetly.
* @param int $usTimeout Microseconds to add to the waiting time.
* @param Registry $reg An optional registry to sync the
* response with.
*
* @see getType()
* @see getArgument()
*/
public function __construct(
Communicator $com,
$asStream = false,
$sTimeout = 0,
$usTimeout = null,
Registry $reg = null
) {
if (null === $reg) {
if ($com->getTransmitter()->isPersistent()) {
$old = $com->getTransmitter()
->lock(T\Stream::DIRECTION_RECEIVE);
try {
$this->_receive($com, $asStream, $sTimeout, $usTimeout);
} catch (E $e) {
$com->getTransmitter()->lock($old, true);
throw $e;
}
$com->getTransmitter()->lock($old, true);
} else {
$this->_receive($com, $asStream, $sTimeout, $usTimeout);
}
} else {
while (null === ($response = $reg->getNextResponse())) {
$newResponse = new self($com, true, $sTimeout, $usTimeout);
$tagInfo = $reg::parseTag($newResponse->getTag());
$newResponse->setTag($tagInfo[1]);
if (!$reg->add($newResponse, $tagInfo[0])) {
$response = $newResponse;
break;
}
}
$this->_type = $response->_type;
$this->attributes = $response->attributes;
$this->unrecognizedWords = $response->unrecognizedWords;
$this->setTag($response->getTag());
if (!$asStream) {
foreach ($this->attributes as $name => $value) {
$this->setAttribute(
$name,
stream_get_contents($value)
);
}
foreach ($response->unrecognizedWords as $i => $value) {
$this->unrecognizedWords[$i] = stream_get_contents($value);
}
}
}
}
/**
* Extracts a new response from a communicator.
*
* This is the function that performs the actual receiving, while the
* constructor is also involved in locks and registry sync.
*
* @param Communicator $com The communicator from which to extract
* the new response.
* @param bool $asStream Whether to populate the argument values
* with streams instead of strings.
* @param int $sTimeout If a response is not immediatly
* available, wait this many seconds. If NULL, wait indefinetly.
* Note that if an empty sentence is received, the timeout will be
* reset for another sentence receiving.
* @param int $usTimeout Microseconds to add to the waiting time.
*
* @return void
*/
private function _receive(
Communicator $com,
$asStream = false,
$sTimeout = 0,
$usTimeout = null
) {
do {
if (!$com->getTransmitter()->isDataAwaiting(
$sTimeout,
$usTimeout
)) {
throw new SocketException(
'No data within the time limit',
SocketException::CODE_NO_DATA
);
}
$type = $com->getNextWord();
} while ('' === $type);
$this->setType($type);
if ($asStream) {
for ($word = $com->getNextWordAsStream(), fseek($word, 0, SEEK_END);
ftell($word) !== 0;
$word = $com->getNextWordAsStream(), fseek(
$word,
0,
SEEK_END
)) {
rewind($word);
$ind = fread($word, 1);
if ('=' === $ind || '.' === $ind) {
$prefix = stream_get_line($word, null, '=');
}
if ('=' === $ind) {
$value = fopen('php://temp', 'r+b');
$bytesCopied = ftell($word);
while (!feof($word)) {
$bytesCopied += stream_copy_to_stream(
$word,
$value,
0xFFFFF,
$bytesCopied
);
}
rewind($value);
$this->setAttribute($prefix, $value);
continue;
}
if ('.' === $ind && 'tag' === $prefix) {
$this->setTag(stream_get_contents($word, -1, -1));
continue;
}
rewind($word);
$this->unrecognizedWords[] = $word;
}
} else {
for ($word = $com->getNextWord(); '' !== $word;
$word = $com->getNextWord()) {
if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
$this->setAttribute($matches[1], $matches[2]);
} elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
$this->setTag($matches[1]);
} else {
$this->unrecognizedWords[] = $word;
}
}
}
}
/**
* Sets the response type.
*
* Sets the response type. Valid values are the TYPE_* constants.
*
* @param string $type The new response type.
*
* @return $this The response object.
* @see getType()
*/
protected function setType($type)
{
switch ($type) {
case self::TYPE_FINAL:
case self::TYPE_DATA:
case self::TYPE_ERROR:
case self::TYPE_FATAL:
$this->_type = $type;
return $this;
default:
throw new UnexpectedValueException(
'Unrecognized response type.',
UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN,
null,
$type
);
}
}
/**
* Gets the response type.
*
* @return string The response type.
* @see setType()
*/
public function getType()
{
return $this->_type;
}
/**
* Gets the value of an argument.
*
* @param string $name The name of the argument.
*
* @return string|resource|null The value of the specified argument.
* Returns NULL if such an argument is not set.
* @deprecated 1.0.0b5 Use {@link static::getProperty()} instead.
* This method will be removed upon final release, and is currently
* left standing merely because it can't be easily search&replaced in
* existing code, due to the fact the name "getArgument()" is shared
* with {@link Request::getArgument()}, which is still valid.
* @codeCoverageIgnore
*/
public function getArgument($name)
{
trigger_error(
'Response::getArgument() is deprecated in favor of ' .
'Response::getProperty() (but note that Request::getArgument() ' .
'is still valid)',
E_USER_DEPRECATED
);
return $this->getAttribute($name);
}
/**
* Gets the value of a property.
*
* @param string $name The name of the property.
*
* @return string|resource|null The value of the specified property.
* Returns NULL if such a property is not set.
*/
public function getProperty($name)
{
return parent::getAttribute($name);
}
/**
* Gets a list of unrecognized words.
*
* @return array The list of unrecognized words.
*/
public function getUnrecognizedWords()
{
return $this->unrecognizedWords;
}
/**
* Counts the number of arguments or words.
*
* @param int $mode The counter mode.
* Either COUNT_NORMAL or COUNT_RECURSIVE.
* When in normal mode, counts the number of arguments.
* When in recursive mode, counts the number of API words.
*
* @return int The number of arguments/words.
*/
public function count($mode = COUNT_NORMAL)
{
$result = parent::count($mode);
if ($mode !== COUNT_NORMAL) {
$result += count($this->unrecognizedWords);
}
return $result;
}
}

View File

@ -0,0 +1,569 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Implemented by this class.
*/
use ArrayAccess;
/**
* Implemented by this class.
*/
use Countable;
/**
* Implemented by this class.
*/
use SeekableIterator;
/**
* Represents a collection of RouterOS responses.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*
* @method string getType()
* Calls {@link Response::getType()}
* on the response pointed by the pointer.
* @method string[] getUnrecognizedWords()
* Calls {@link Response::getUnrecognizedWords()}
* on the response pointed by the pointer.
* @method string|resource|null getProperty(string $name)
* Calls {@link Response::getProperty()}
* on the response pointed by the pointer.
* @method string getTag()
* Calls {@link Response::getTag()}
* on the response pointed by the pointer.
*/
class ResponseCollection implements ArrayAccess, SeekableIterator, Countable
{
/**
* @var array An array with all {@link Response} objects.
*/
protected $responses = array();
/**
* @var array An array with each {@link Response} object's type.
*/
protected $responseTypes = array();
/**
* @var array An array with each {@link Response} object's tag.
*/
protected $responseTags = array();
/**
* @var array An array with positions of responses, based on an property
* name. The name of each property is the array key, and the array value
* is another array where the key is the value for that property, and
* the value is the posistion of the response. For performance reasons,
* each key is built only when {@link static::setIndex()} is called with
* that property, and remains available for the lifetime of this
* collection.
*/
protected $responsesIndex = array();
/**
* @var array An array with all distinct properties across all
* {@link Response} objects. Created at the first call of
* {@link static::getPropertyMap()}.
*/
protected $propertyMap = null;
/**
* @var int A pointer, as required by SeekableIterator.
*/
protected $position = 0;
/**
* @var string|null Name of property to use as index. NULL when disabled.
*/
protected $index = null;
/**
* @var array Criterias used by {@link compare()} to determine the order
* between two respones. See {@link orderBy()} for a detailed
* description of this array's format.
*/
protected $compareBy = array();
/**
* Creates a new collection.
*
* @param array $responses An array of responses, in network order.
*/
public function __construct(array $responses)
{
$pos = 0;
foreach ($responses as $response) {
if ($response instanceof Response) {
$this->responseTypes[$pos] = $response->getType();
$this->responseTags[$pos] = $response->getTag();
$this->responses[$pos++] = $response;
}
}
}
/**
* A shorthand gateway.
*
* This is a magic PHP method that allows you to call the object as a
* function. Depending on the argument given, one of the other functions in
* the class is invoked and its returned value is returned by this function.
*
* @param int|string|null $offset The offset of the response to seek to.
* If the offset is negative, seek to that relative to the end.
* If the collection is indexed, you can also supply a value to seek to.
* Setting NULL will get the current response's interator.
*
* @return Response|ArrayObject The {@link Response} at the specified
* offset, the current response's iterator (which is an ArrayObject)
* when NULL is given, or FALSE if the offset is invalid
* or the collection is empty.
*/
public function __invoke($offset = null)
{
return null === $offset
? $this->current()->getIterator()
: $this->seek($offset);
}
/**
* Sets a property to be usable as a key in the collection.
*
* @param string|null $name The name of the property to use. Future calls
* that accept a position will then also be able to search values of
* that property for a matching value.
* Specifying NULL will disable such lookups (as is by default).
* Note that in case this value occures multiple times within the
* collection, only the last matching response will be accessible by
* that value.
*
* @return $this The object itself.
*/
public function setIndex($name)
{
if (null !== $name) {
$name = (string)$name;
if (!isset($this->responsesIndex[$name])) {
$this->responsesIndex[$name] = array();
foreach ($this->responses as $pos => $response) {
$val = $response->getProperty($name);
if (null !== $val) {
$this->responsesIndex[$name][$val] = $pos;
}
}
}
}
$this->index = $name;
return $this;
}
/**
* Gets the name of the property used as an index.
*
* @return string|null Name of property used as index. NULL when disabled.
*/
public function getIndex()
{
return $this->index;
}
/**
* Gets the whole collection as an array.
*
* @param bool $useIndex Whether to use the index values as keys for the
* resulting array.
*
* @return array An array with all responses, in network order.
*/
public function toArray($useIndex = false)
{
if ($useIndex) {
$positions = $this->responsesIndex[$this->index];
asort($positions, SORT_NUMERIC);
$positions = array_flip($positions);
return array_combine(
$positions,
array_intersect_key($this->responses, $positions)
);
}
return $this->responses;
}
/**
* Counts the responses/words in the collection.
*
* @param int $mode The counter mode.
* Either COUNT_NORMAL or COUNT_RECURSIVE.
* When in normal mode, counts the number of responses.
* When in recursive mode, counts the total number of API words.
*
* @return int The number of responses in the collection.
*/
public function count($mode = COUNT_NORMAL)
{
if ($mode !== COUNT_NORMAL) {
$result = 0;
foreach ($this->responses as $response) {
$result += $response->count($mode);
}
return $result;
} else {
return count($this->responses);
}
}
/**
* Checks if an offset exists.
*
* @param int|string $offset The offset to check. If the
* collection is indexed, you can also supply a value to check.
* Note that negative numeric offsets are NOT accepted.
*
* @return bool TRUE if the offset exists, FALSE otherwise.
*/
public function offsetExists($offset)
{
return is_int($offset)
? array_key_exists($offset, $this->responses)
: array_key_exists($offset, $this->responsesIndex[$this->index]);
}
/**
* Gets a {@link Response} from a specified offset.
*
* @param int|string $offset The offset of the desired response. If the
* collection is indexed, you can also supply the value to search for.
*
* @return Response The response at the specified offset.
*/
public function offsetGet($offset)
{
return is_int($offset)
? $this->responses[$offset >= 0
? $offset
: count($this->responses) + $offset]
: $this->responses[$this->responsesIndex[$this->index][$offset]];
}
/**
* N/A
*
* This method exists only because it is required for ArrayAccess. The
* collection is read only.
*
* @param int|string $offset N/A
* @param Response $value N/A
*
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function offsetSet($offset, $value)
{
}
/**
* N/A
*
* This method exists only because it is required for ArrayAccess. The
* collection is read only.
*
* @param int|string $offset N/A
*
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function offsetUnset($offset)
{
}
/**
* Resets the pointer to 0, and returns the first response.
*
* @return Response The first response in the collection, or FALSE if the
* collection is empty.
*/
public function rewind()
{
return $this->seek(0);
}
/**
* Moves the position pointer to a specified position.
*
* @param int|string $position The position to move to. If the collection is
* indexed, you can also supply a value to move the pointer to.
* A non-existent index will move the pointer to "-1".
*
* @return Response The {@link Response} at the specified position, or FALSE
* if the specified position is not valid.
*/
public function seek($position)
{
$this->position = is_int($position)
? ($position >= 0
? $position
: count($this->responses) + $position)
: ($this->offsetExists($position)
? $this->responsesIndex[$this->index][$position]
: -1);
return $this->current();
}
/**
* Moves the pointer forward by 1, and gets the next response.
*
* @return Response The next {@link Response} object, or FALSE if the
* position is not valid.
*/
public function next()
{
++$this->position;
return $this->current();
}
/**
* Gets the response at the current pointer position.
*
* @return Response The response at the current pointer position, or FALSE
* if the position is not valid.
*/
public function current()
{
return $this->valid() ? $this->responses[$this->position] : false;
}
/**
* Moves the pointer backwards by 1, and gets the previous response.
*
* @return Response The next {@link Response} object, or FALSE if the
* position is not valid.
*/
public function prev()
{
--$this->position;
return $this->current();
}
/**
* Moves the pointer to the last valid position, and returns the last
* response.
*
* @return Response The last response in the collection, or FALSE if the
* collection is empty.
*/
public function end()
{
$this->position = count($this->responses) - 1;
return $this->current();
}
/**
* Gets the key at the current pointer position.
*
* @return int The key at the current pointer position, i.e. the pointer
* position itself, or FALSE if the position is not valid.
*/
public function key()
{
return $this->valid() ? $this->position : false;
}
/**
* Checks if the pointer is still pointing to an existing offset.
*
* @return bool TRUE if the pointer is valid, FALSE otherwise.
*/
public function valid()
{
return $this->offsetExists($this->position);
}
/**
* Gets all distinct property names.
*
* Gets all distinct property names across all responses.
*
* @return array An array with all distinct property names as keys, and the
* indexes at which they occur as values.
*/
public function getPropertyMap()
{
if (null === $this->propertyMap) {
$properties = array();
foreach ($this->responses as $index => $response) {
$names = array_keys($response->getIterator()->getArrayCopy());
foreach ($names as $name) {
if (!isset($properties[$name])) {
$properties[$name] = array();
}
$properties[$name][] = $index;
}
}
$this->propertyMap = $properties;
}
return $this->propertyMap;
}
/**
* Gets all responses of a specified type.
*
* @param string $type The response type to filter by. Valid values are the
* Response::TYPE_* constants.
*
* @return static A new collection with responses of the
* specified type.
*/
public function getAllOfType($type)
{
$result = array();
foreach (array_keys($this->responseTypes, $type, true) as $index) {
$result[] = $this->responses[$index];
}
return new static($result);
}
/**
* Gets all responses with a specified tag.
*
* @param string $tag The tag to filter by.
*
* @return static A new collection with responses having the
* specified tag.
*/
public function getAllTagged($tag)
{
$result = array();
foreach (array_keys($this->responseTags, $tag, true) as $index) {
$result[] = $this->responses[$index];
}
return new static($result);
}
/**
* Order resones by criteria.
*
* @param mixed[] $criteria The criteria to order respones by. It takes the
* form of an array where each key is the name of the property to use
* as (N+1)th sorting key. The value of each member can be either NULL
* (for that property, sort normally in ascending order), a single sort
* order constant (SORT_ASC or SORT_DESC) to sort normally in the
* specified order, an array where the first member is an order
* constant, and the second one is sorting flags (same as built in PHP
* array functions) or a callback.
* If a callback is provided, it must accept two arguments
* (the two values to be compared), and return -1, 0 or 1 if the first
* value is respectively less than, equal to or greather than the second
* one.
* Each key of $criteria can also be numeric, in which case the
* value is the name of the property, and sorting is done normally in
* ascending order.
*
* @return static A new collection with the responses sorted in the
* specified order.
*/
public function orderBy(array $criteria)
{
$this->compareBy = $criteria;
$sortedResponses = $this->responses;
usort($sortedResponses, array($this, 'compare'));
return new static($sortedResponses);
}
/**
* Calls a method of the response pointed by the pointer.
*
* Calls a method of the response pointed by the pointer. This is a magic
* PHP method, thanks to which any function you call on the collection that
* is not defined will be redirected to the response.
*
* @param string $method The name of the method to call.
* @param array $args The arguments to pass to the method.
*
* @return mixed Whatever the called function returns.
*/
public function __call($method, array $args)
{
return call_user_func_array(
array($this->current(), $method),
$args
);
}
/**
* Compares two respones.
*
* Compares two respones, based on criteria defined in
* {@link static::$compareBy}.
*
* @param Response $itemA The response to compare.
* @param Response $itemB The response to compare $a against.
*
* @return int Returns 0 if the two respones are equal according to every
* criteria specified, -1 if $a should be placed before $b, and 1 if $b
* should be placed before $a.
*/
protected function compare(Response $itemA, Response $itemB)
{
foreach ($this->compareBy as $name => $spec) {
if (!is_string($name)) {
$name = $spec;
$spec = null;
}
$members = array(
0 => $itemA->getProperty($name),
1 => $itemB->getProperty($name)
);
if (is_callable($spec)) {
uasort($members, $spec);
} elseif ($members[0] === $members[1]) {
continue;
} else {
$flags = SORT_REGULAR;
$order = SORT_ASC;
if (is_array($spec)) {
list($order, $flags) = $spec;
} elseif (null !== $spec) {
$order = $spec;
}
if (SORT_ASC === $order) {
asort($members, $flags);
} else {
arsort($members, $flags);
}
}
return (key($members) === 0) ? -1 : 1;
}
return 0;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
/**
* Base of this class.
*/
use RuntimeException;
/**
* Exception thrown when something goes wrong with the connection.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class SocketException extends RuntimeException implements Exception
{
const CODE_SERVICE_INCOMPATIBLE = 10200;
const CODE_CONNECTION_FAIL = 100;
const CODE_QUERY_SEND_FAIL = 30600;
const CODE_REQUEST_SEND_FAIL = 40900;
const CODE_NO_DATA = 50000;
}

View File

@ -0,0 +1,88 @@
<?php
/**
* RouterOS API client implementation.
*
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0b5
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\RouterOS;
use UnexpectedValueException as U;
/**
* Exception thrown when encountering an invalid value in a function argument.
*
* @category Net
* @package PEAR2_Net_RouterOS
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_RouterOS
*/
class UnexpectedValueException extends U implements Exception
{
const CODE_CALLBACK_INVALID = 10502;
const CODE_ACTION_UNKNOWN = 30100;
const CODE_RESPONSE_TYPE_UNKNOWN = 50100;
/**
* @var mixed The unexpected value.
*/
private $_value;
/**
* Creates a new UnexpectedValueException.
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param \Exception $previous The previous exception used for the exception
* chaining.
* @param mixed $value The unexpected value.
*/
public function __construct(
$message,
$code = 0,
$previous = null,
$value = null
) {
parent::__construct($message, $code, $previous);
$this->_value = $value;
}
/**
* Gets the unexpected value.
*
* @return mixed The unexpected value.
*/
public function getValue()
{
return $this->_value;
}
// @codeCoverageIgnoreStart
// String representation is not reliable in testing
/**
* Returns a string representation of the exception.
*
* @return string The exception as a string.
*/
public function __toString()
{
return parent::__toString() . "\nValue:{$this->_value}";
}
// @codeCoverageIgnoreEnd
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* Generic exception class of this package.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
interface Exception
{
}

View File

@ -0,0 +1,229 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* A filter collection.
*
* Represents a collection of stream filters.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
* @see Client
*/
class FilterCollection implements \SeekableIterator, \Countable
{
/**
* @var array The filter collection itself.
*/
protected $filters = array();
/**
* @var int A pointer, as required by SeekableIterator.
*/
protected $position = 0;
/**
* Appends a filter to the collection
*
* @param string $name The name of the filter.
* @param array $params An array of parameters for the filter.
*
* @return $this The collection itself.
*/
public function append($name, array $params = array())
{
$this->filters[] = array((string) $name, $params);
return $this;
}
/**
* Inserts the filter before a position.
*
* Inserts the specified filter before a filter at a specified position. The
* new filter takes the specified position, while previous filters are moved
* forward by one.
*
* @param int $position The position before which the filter will be
* inserted.
* @param string $name The name of the filter.
* @param array $params An array of parameters for the filter.
*
* @return $this The collection itself.
*/
public function insertBefore($position, $name, array $params = array())
{
$position = (int) $position;
if ($position <= 0) {
$this->filters = array_merge(
array(0 => array((string) $name, $params)),
$this->filters
);
return $this;
}
if ($position > count($this->filters)) {
return $this->append($name, $params);
}
$this->filters = array_merge(
array_slice($this->filters, 0, $position),
array(0 => array((string) $name, $params)),
array_slice($this->filters, $position)
);
return $this;
}
/**
* Removes a filter at a specified position.
*
* @param int $position The position from which to remove a filter.
*
* @return $this The collection itself.
*/
public function removeAt($position)
{
unset($this->filters[$position]);
$this->filters = array_values($this->filters);
return $this;
}
/**
* Clears the collection
*
* @return $this The collection itself.
*/
public function clear()
{
$this->filters = array();
return $this;
}
/**
* Gets the number of filters in the collection.
*
* @return int The number of filters in the collection.
*/
public function count()
{
return count($this->filters);
}
/**
* Resets the pointer to 0.
*
* @return bool TRUE if the collection is not empty, FALSE otherwise.
*/
public function rewind()
{
return $this->seek(0);
}
/**
* Moves the pointer to a specified position.
*
* @param int $position The position to move to.
*
* @return bool TRUE if the specified position is valid, FALSE otherwise.
*/
public function seek($position)
{
$this->position = $position;
return $this->valid();
}
/**
* Gets the current position.
*
* @return int The current position.
*/
public function getCurrentPosition()
{
return $this->position;
}
/**
* Moves the pointer forward by 1.
*
* @return bool TRUE if the new position is valid, FALSE otherwise.
*/
public function next()
{
++$this->position;
return $this->valid();
}
/**
* Gets the filter name at the current pointer position.
*
* @return string The name of the filter at the current position.
*/
public function key()
{
return $this->valid() ? $this->filters[$this->position][0] : false;
}
/**
* Gets the filter parameters at the current pointer position.
*
* @return array An array of parameters for the filter at the current
* position.
*/
public function current()
{
return $this->valid() ? $this->filters[$this->position][1] : false;
}
/**
* Moves the pointer backwards by 1.
*
* @return bool TRUE if the new position is valid, FALSE otherwise.
*/
public function prev()
{
--$this->position;
return $this->valid();
}
/**
* Moves the pointer to the last valid position.
*
* @return bool TRUE if the collection is not empty, FALSE otherwise.
*/
public function end()
{
$this->position = count($this->filters) - 1;
return $this->valid();
}
/**
* Checks if the pointer is still pointing to an existing offset.
*
* @return bool TRUE if the pointer is valid, FALSE otherwise.
*/
public function valid()
{
return array_key_exists($this->position, $this->filters);
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* Exception thrown when something goes wrong when dealing with locks.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class LockException extends \RuntimeException implements Exception
{
}

View File

@ -0,0 +1,181 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* A network transmitter.
*
* This is a convinience wrapper for network streams. Used to ensure data
* integrity.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
abstract class NetworkStream extends Stream
{
/**
* Used in {@link setCrypto()} to disable encryption.
*/
const CRYPTO_OFF = '';
/**
* Used in {@link setCrypto()} to set encryption to either SSLv2 or SSLv3,
* depending on what the other end supports.
*/
const CRYPTO_SSL = 'SSLv23';
/**
* Used in {@link setCrypto()} to set encryption to SSLv2.
*/
const CRYPTO_SSL2 = 'SSLv2';
/**
* Used in {@link setCrypto()} to set encryption to SSLv3.
*/
const CRYPTO_SSL3 = 'SSLv3';
/**
* Used in {@link setCrypto()} to set encryption to TLS (exact version
* negotiated between 1.0 and 1.2).
*/
const CRYPTO_TLS = 'TLS';
/**
* @var string The type of stream. Can be either "_CLIENT" or "_SERVER".
* Used to complement the encryption type. Must be set by child classes
* for {@link setCrypto()} to work properly.
*/
protected $streamType = '';
/**
* @var string The current cryptography setting.
*/
protected $crypto = '';
/**
* Wraps around the specified stream.
*
* @param resource $stream The stream to wrap around.
*/
public function __construct($stream)
{
parent::__construct($stream, true);
}
/**
* Gets the current cryptography setting.
*
* @return string One of this class' CRYPTO_* constants.
*/
public function getCrypto()
{
return $this->crypto;
}
/**
* Sets the current connection's cryptography setting.
*
* @param string $type The encryption type to set. Must be one of this
* class' CRYPTO_* constants.
*
* @return boolean TRUE on success, FALSE on failure.
*/
public function setCrypto($type)
{
if (self::CRYPTO_OFF === $type) {
$result = stream_socket_enable_crypto($this->stream, false);
} else {
$result = stream_socket_enable_crypto(
$this->stream,
true,
constant("STREAM_CRYPTO_METHOD_{$type}{$this->streamType}")
);
}
if ($result) {
$this->crypto = $type;
}
return $result;
}
/**
* Checks whether the stream is available for operations.
*
* @return bool TRUE if the stream is available, FALSE otherwise.
*/
public function isAvailable()
{
if (parent::isStream($this->stream)) {
if ($this->isBlocking && feof($this->stream)) {
return false;
}
$meta = stream_get_meta_data($this->stream);
return !$meta['eof'];
}
return false;
}
/**
* Sets the size of a stream's buffer.
*
* @param int $size The desired size of the buffer, in bytes.
* @param string $direction The buffer of which direction to set. Valid
* values are the DIRECTION_* constants.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setBuffer($size, $direction = self::DIRECTION_ALL)
{
$result = parent::setBuffer($size, $direction);
if (self::DIRECTION_SEND === $direction
&& function_exists('stream_set_chunk_size') && !$result
) {
return false !== @stream_set_chunk_size($this->stream, $size);
}
return $result;
}
/**
* Shutdown a full-duplex connection
*
* Shutdowns (partially or not) a full-duplex connection.
*
* @param string $direction The direction for which to disable further
* communications.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function shutdown($direction = self::DIRECTION_ALL)
{
$directionMap = array(
self::DIRECTION_ALL => STREAM_SHUT_RDWR,
self::DIRECTION_SEND => STREAM_SHUT_WR,
self::DIRECTION_RECEIVE => STREAM_SHUT_RD
);
return array_key_exists($direction, $directionMap)
&& stream_socket_shutdown($this->stream, $directionMap[$direction]);
}
}

View File

@ -0,0 +1,124 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* Used to enable any exception in chaining.
*/
use Exception as E;
/**
* Exception thrown when something goes wrong with the connection.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class SocketException extends StreamException
{
/**
* @var int The system level error code.
*/
protected $errorNo;
/**
* @var string The system level error message.
*/
protected $errorStr;
/**
* Creates a new socket exception.
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param E|null $previous Previous exception thrown,
* or NULL if there is none.
* @param int|string|resource|null $fragment The fragment up until the
* point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
* NULL if the failure occured before the operation started.
* @param int $errorNo The system level error number.
* @param string $errorStr The system level
* error message.
*/
public function __construct(
$message = '',
$code = 0,
E $previous = null,
$fragment = null,
$errorNo = null,
$errorStr = null
) {
parent::__construct($message, $code, $previous, $fragment);
$this->errorNo = $errorNo;
$this->errorStr = $errorStr;
}
/**
* Gets the system level error code on the socket.
*
* @return int The system level error number.
*/
public function getSocketErrorNumber()
{
return $this->errorNo;
}
// @codeCoverageIgnoreStart
// Unreliable in testing.
/**
* Gets the system level error message on the socket.
*
* @return string The system level error message.
*/
public function getSocketErrorMessage()
{
return $this->errorStr;
}
/**
* Returns a string representation of the exception.
*
* @return string The exception as a string.
*/
public function __toString()
{
$result = parent::__toString();
if (null !== $this->getSocketErrorNumber()) {
$result .= "\nSocket error number:" . $this->getSocketErrorNumber();
}
if (null !== $this->getSocketErrorMessage()) {
$result .= "\nSocket error message:"
. $this->getSocketErrorMessage();
}
return $result;
}
// @codeCoverageIgnoreEnd
}

View File

@ -0,0 +1,605 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
use Exception as E;
/**
* A stream transmitter.
*
* This is a convinience wrapper for stream functionality. Used to ensure data
* integrity. Designed for TCP sockets, but it has intentionally been made to
* accept any stream.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class Stream
{
/**
* Used to stop settings in either direction being applied.
*/
const DIRECTION_NONE = 0;
/**
* Used to apply settings only to receiving.
*/
const DIRECTION_RECEIVE = 1;
/**
* Used to apply settings only to sending.
*/
const DIRECTION_SEND = 2;
/**
* Used to apply settings to both sending and receiving.
*/
const DIRECTION_ALL = 3;
/**
* @var resource The stream to wrap around.
*/
protected $stream;
/**
* @var bool Whether to automaticaly close the stream on
* object destruction if it's not a persistent one. Setting this to
* FALSE may be useful if you're only using this class "part time",
* while setting it to TRUE might be useful if you're doing some
* "on offs".
*/
protected $autoClose = false;
/**
* @var bool A flag that tells whether or not the stream is persistent.
*/
protected $persist;
/**
* @var bool Whether the wrapped stream is in blocking mode or not.
*/
protected $isBlocking = true;
/**
* @var array An associative array with the chunk size of each direction.
* Key is the direction, value is the size in bytes as integer.
*/
protected $chunkSize = array(
self::DIRECTION_SEND => 0xFFFFF, self::DIRECTION_RECEIVE => 0xFFFFF
);
/**
* Wraps around the specified stream.
*
* @param resource $stream The stream to wrap around.
* @param bool $autoClose Whether to automaticaly close the stream on
* object destruction if it's not a persistent one. Setting this to
* FALSE may be useful if you're only using this class "part time",
* while setting it to TRUE might be useful if you're doing some
* "on offs".
*
* @see static::isFresh()
*/
public function __construct($stream, $autoClose = false)
{
if (!self::isStream($stream)) {
throw $this->createException('Invalid stream supplied.', 1);
}
$this->stream = $stream;
$this->autoClose = (bool) $autoClose;
$this->persist = (bool) preg_match(
'#\s?persistent\s?#sm',
get_resource_type($stream)
);
$meta = stream_get_meta_data($stream);
$this->isBlocking = isset($meta['blocked']) ? $meta['blocked'] : true;
}
/**
* PHP error handler for connection errors.
*
* @param string $level Level of PHP error raised. Ignored.
* @param string $message Message raised by PHP.
*
* @return void
* @throws SocketException That's how the error is handled.
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function handleError($level, $message)
{
throw $this->createException($message, 0);
}
/**
* Checks if a given variable is a stream resource.
*
* @param mixed $var The variable to check.
*
* @return bool TRUE on success, FALSE on failure.
*/
public static function isStream($var)
{
return is_resource($var)
&& (bool) preg_match('#\s?stream$#sm', get_resource_type($var));
}
/**
* Checks whether the wrapped stream is fresh.
*
* Checks whether the wrapped stream is fresh. A stream is considered fresh
* if there hasn't been any activity on it. Particularly useful for
* detecting reused persistent connections.
*
* @return bool TRUE if the socket is fresh, FALSE otherwise.
*/
public function isFresh()
{
return ftell($this->stream) === 0;
}
/**
* Checks whether the wrapped stream is a persistent one.
*
* @return bool TRUE if the stream is a persistent one, FALSE otherwise.
*/
public function isPersistent()
{
return $this->persist;
}
/**
* Checks whether the wrapped stream is a blocking one.
*
* @return bool TRUE if the stream is a blocking one, FALSE otherwise.
*/
public function isBlocking()
{
return $this->isBlocking;
}
/**
* Sets blocking mode.
*
* @param bool $block Sets whether the stream is in blocking mode.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setIsBlocking($block)
{
$block = (bool)$block;
if (stream_set_blocking($this->stream, (int)$block)) {
$this->isBlocking = $block;
return true;
}
return false;
}
/**
* Sets the timeout for the stream.
*
* @param int $seconds Timeout in seconds.
* @param int $microseconds Timeout in microseconds to be added to the
* seconds.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setTimeout($seconds, $microseconds = 0)
{
return stream_set_timeout($this->stream, $seconds, $microseconds);
}
/**
* Sets the size of a stream's buffer.
*
* @param int $size The desired size of the buffer, in bytes.
* @param string $direction The buffer of which direction to set. Valid
* values are the DIRECTION_* constants.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setBuffer($size, $direction = self::DIRECTION_ALL)
{
switch($direction) {
case self::DIRECTION_SEND:
return stream_set_write_buffer($this->stream, $size) === 0;
case self::DIRECTION_RECEIVE:
return stream_set_read_buffer($this->stream, $size) === 0;
case self::DIRECTION_ALL:
return $this->setBuffer($size, self::DIRECTION_RECEIVE)
&& $this->setBuffer($size, self::DIRECTION_SEND);
}
return false;
}
/**
* Sets the size of the chunk.
*
* To ensure data integrity, as well as to allow for lower memory
* consumption, data is sent/received in chunks. This function
* allows you to set the size of each chunk. The default is 0xFFFFF.
*
* @param int $size The desired size of the chunk, in bytes.
* @param string $direction The chunk of which direction to set. Valid
* values are the DIRECTION_* constants.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function setChunk($size, $direction = self::DIRECTION_ALL)
{
$size = (int) $size;
if ($size <= 0) {
return false;
}
switch($direction) {
case self::DIRECTION_SEND:
case self::DIRECTION_RECEIVE:
$this->chunkSize[$direction] = $size;
return true;
case self::DIRECTION_ALL:
$this->chunkSize[self::DIRECTION_SEND]
= $this->chunkSize[self::DIRECTION_RECEIVE] = $size;
return true;
}
return false;
}
/**
* Gets the size of the chunk.
*
* @param string $direction The chunk of which direction to get. Valid
* values are the DIRECTION_* constants.
*
* @return int|array|false The chunk size in bytes,
* or an array of chunk sizes with the directions as keys.
* FALSE on invalid direction.
*/
public function getChunk($direction = self::DIRECTION_ALL)
{
switch($direction) {
case self::DIRECTION_SEND:
case self::DIRECTION_RECEIVE:
return $this->chunkSize[$direction];
case self::DIRECTION_ALL:
return $this->chunkSize;
}
return false;
}
/**
* Sends a string or stream over the wrapped stream.
*
* Sends a string or stream over the wrapped stream. If a seekable stream is
* provided, it will be seeked back to the same position it was passed as,
* regardless of the $offset parameter.
*
* @param string|resource $contents The string or stream to send.
* @param int $offset The offset from which to start sending.
* If a stream is provided, and this is set to NULL, sending will start
* from the current stream position.
* @param int $length The maximum length to send. If omitted,
* the string/stream will be sent to its end.
*
* @return int The number of bytes sent.
*/
public function send($contents, $offset = null, $length = null)
{
$bytes = 0;
$chunkSize = $this->chunkSize[self::DIRECTION_SEND];
$lengthIsNotNull = null !== $length;
$offsetIsNotNull = null !== $offset;
if (self::isStream($contents)) {
if ($offsetIsNotNull) {
$oldPos = ftell($contents);
fseek($contents, $offset, SEEK_SET);
}
while (!feof($contents)) {
if ($lengthIsNotNull
&& 0 === $chunkSize = min($chunkSize, $length - $bytes)
) {
break;
}
$bytesNow = @fwrite(
$this->stream,
fread($contents, $chunkSize)
);
if (0 != $bytesNow) {
$bytes += $bytesNow;
} elseif ($this->isBlocking || false === $bytesNow) {
throw $this->createException(
'Failed while sending stream.',
2,
null,
$bytes
);
} else {
usleep(300000);
}
$this->isAcceptingData(null);
}
if ($offsetIsNotNull) {
fseek($contents, $oldPos, SEEK_SET);
} else {
fseek($contents, -$bytes, SEEK_CUR);
}
} else {
$contents = (string) $contents;
if ($offsetIsNotNull) {
$contents = substr($contents, $offset);
}
if ($lengthIsNotNull) {
$contents = substr($contents, 0, $length);
}
$bytesToSend = (double) sprintf('%u', strlen($contents));
while ($bytes < $bytesToSend) {
$bytesNow = @fwrite(
$this->stream,
substr($contents, $bytes, $chunkSize)
);
if (0 != $bytesNow) {
$bytes += $bytesNow;
} elseif ($this->isBlocking || false === $bytesNow) {
throw $this->createException(
'Failed while sending string.',
3,
null,
$bytes
);
} else {
usleep(300000);
}
$this->isAcceptingData(null);
}
}
return $bytes;
}
/**
* Reads from the wrapped stream to receive.
*
* Reads from the wrapped stream to receive content as a string.
*
* @param int $length The number of bytes to receive.
* @param string $what Descriptive string about what is being received
* (used in exception messages).
*
* @return string The received content.
*/
public function receive($length, $what = 'data')
{
$result = '';
$chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE];
while ($length > 0) {
while ($this->isAvailable()) {
$fragment = fread($this->stream, min($length, $chunkSize));
if ('' != $fragment) {
$length -= strlen($fragment);
$result .= $fragment;
continue 2;
} elseif (!$this->isBlocking && !(false === $fragment)) {
usleep(3000);
continue 2;
}
}
throw $this->createException(
"Failed while receiving {$what}",
4,
null,
$result
);
}
return $result;
}
/**
* Reads from the wrapped stream to receive.
*
* Reads from the wrapped stream to receive content as a stream.
*
* @param int $length The number of bytes to receive.
* @param FilterCollection $filters A collection of filters to apply to the
* stream while receiving. Note that the filters will not be present on
* the stream after receiving is done.
* @param string $what Descriptive string about what is being
* received (used in exception messages).
*
* @return resource The received content.
*/
public function receiveStream(
$length,
FilterCollection $filters = null,
$what = 'stream data'
) {
$result = fopen('php://temp', 'r+b');
$appliedFilters = array();
if (null !== $filters) {
foreach ($filters as $filtername => $params) {
$appliedFilters[] = stream_filter_append(
$result,
$filtername,
STREAM_FILTER_WRITE,
$params
);
}
}
$chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE];
while ($length > 0) {
while ($this->isAvailable()) {
$fragment = fread($this->stream, min($length, $chunkSize));
if ('' != $fragment) {
$length -= strlen($fragment);
fwrite($result, $fragment);
continue 2;
} elseif (!$this->isBlocking && !(false === $fragment)) {
usleep(3000);
continue 2;
}
}
foreach ($appliedFilters as $filter) {
stream_filter_remove($filter);
}
rewind($result);
throw $this->createException(
"Failed while receiving {$what}",
5,
null,
$result
);
}
foreach ($appliedFilters as $filter) {
stream_filter_remove($filter);
}
rewind($result);
return $result;
}
/**
* Checks whether the stream is available for operations.
*
* For network streams, this means whether the other end has closed the
* connection.
*
* @return bool TRUE if the stream is available, FALSE otherwise.
*/
public function isAvailable()
{
return self::isStream($this->stream) && !feof($this->stream);
}
/**
* Checks whether there is data to be read from the wrapped stream.
*
* @param int|null $sTimeout If theere isn't data awaiting currently,
* wait for it this many seconds for data to arrive. If NULL is
* specified, wait indefinetly for that.
* @param int $usTimeout Microseconds to add to the waiting time.
*
* @return bool TRUE if there is data to be read, FALSE otherwise.
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function isDataAwaiting($sTimeout = 0, $usTimeout = 0)
{
if (self::isStream($this->stream)) {
if (null === $sTimeout && !$this->isBlocking) {
$meta = stream_get_meta_data($this->stream);
return !$meta['eof'];
}
$w = $e = null;
$r = array($this->stream);
return 1 === @/* due to PHP bug #54563 */stream_select(
$r,
$w,
$e,
$sTimeout,
$usTimeout
);
}
return false;
}
/**
* Checks whether the wrapped stream can be written to without a block.
*
* @param int|null $sTimeout If the stream isn't currently accepting data,
* wait for it this many seconds to start accepting data. If NULL is
* specified, wait indefinetly for that.
* @param int $usTimeout Microseconds to add to the waiting time.
*
* @return bool TRUE if the wrapped stream would not block on a write, FALSE
* otherwise.
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function isAcceptingData($sTimeout = 0, $usTimeout = 0)
{
if (self::isStream($this->stream)) {
if (!$this->isBlocking) {
$meta = stream_get_meta_data($this->stream);
return !$meta['eof'];
} elseif (feof($this->stream)) {
return false;
}
$r = $e = null;
$w = array($this->stream);
return 1 === @/* due to PHP bug #54563 */stream_select(
$r,
$w,
$e,
$sTimeout,
$usTimeout
);
}
return false;
}
/**
* Closes the opened stream, unless it's a persistent one.
*/
public function __destruct()
{
if ((!$this->persist) && $this->autoClose) {
$this->close();
}
}
/**
* Closes the opened stream, even if it is a persistent one.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function close()
{
return self::isStream($this->stream) && fclose($this->stream);
}
/**
* Creates a new exception.
*
* Creates a new exception. Used by the rest of the functions in this class.
* Override in derived classes for custom exception handling.
*
* @param string $message The exception message.
* @param int $code The exception code.
* @param E|null $previous Previous exception thrown,
* or NULL if there is none.
* @param int|string|resource|null $fragment The fragment up until the
* point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
*
* @return StreamException The exception to then be thrown.
*/
protected function createException(
$message,
$code = 0,
E $previous = null,
$fragment = null
) {
return new StreamException($message, $code, $previous, $fragment);
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* Base for this exception.
*/
use RuntimeException;
/**
* Used to enable any exception in chaining.
*/
use Exception as E;
/**
* Exception thrown when something goes wrong with the connection.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class StreamException extends RuntimeException implements Exception
{
/**
* @var int|string|resource|null The fragment up until the point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
* NULL if the failure occured before the operation started.
*/
protected $fragment = null;
/**
* Creates a new stream exception.
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param E|null $previous Previous exception thrown,
* or NULL if there is none.
* @param int|string|resource|null $fragment The fragment up until the
* point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
* NULL if the failure occured before the operation started.
*/
public function __construct(
$message,
$code,
E $previous = null,
$fragment = null
) {
parent::__construct($message, $code, $previous);
$this->fragment = $fragment;
}
/**
* Gets the stream fragment.
*
* @return int|string|resource|null The fragment up until the
* point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
* NULL if the failure occured before the operation started.
*/
public function getFragment()
{
return $this->fragment;
}
// @codeCoverageIgnoreStart
// Unreliable in testing.
/**
* Returns a string representation of the exception.
*
* @return string The exception as a string.
*/
public function __toString()
{
$result = parent::__toString();
if (null !== $this->fragment) {
$result .= "\nFragment: ";
if (is_scalar($this->fragment)) {
$result .= (string)$this->fragment;
} else {
$result .= stream_get_contents($this->fragment);
}
}
return $result;
}
// @codeCoverageIgnoreEnd
}

View File

@ -0,0 +1,400 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
/**
* Used for managing persistent connections.
*/
use PEAR2\Cache\SHM;
/**
* Used for matching arbitrary exceptions in
* {@link TcpClient::createException()} and releasing locks properly.
*/
use Exception as E;
/**
* A socket transmitter.
*
* This is a convinience wrapper for socket functionality. Used to ensure data
* integrity.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class TcpClient extends NetworkStream
{
/**
* @var int The error code of the last error on the socket.
*/
protected $errorNo = 0;
/**
* @var string The error message of the last error on the socket.
*/
protected $errorStr = null;
/**
* @var SHM Persistent connection handler. Remains NULL for non-persistent
* connections.
*/
protected $shmHandler = null;
/**
* @var array An array with all connections from this PHP request (as keys)
* and their lock state (as a value).
*/
protected static $lockState = array();
protected static $cryptoScheme = array(
parent::CRYPTO_OFF => 'tcp',
parent::CRYPTO_SSL2 => 'sslv2',
parent::CRYPTO_SSL3 => 'sslv3',
parent::CRYPTO_SSL => 'ssl',
parent::CRYPTO_TLS => 'tls'
);
/**
* @var string The URI of this connection.
*/
protected $uri;
/**
* Creates a new connection with the specified options.
*
* @param string $host Hostname (IP or domain) of the server.
* @param int $port The port on the server.
* @param bool $persist Whether or not the connection should be a
* persistent one.
* @param float $timeout The timeout for the connection.
* @param string $key A string that uniquely identifies the
* connection.
* @param string $crypto Encryption setting. Must be one of the
* NetworkStream::CRYPTO_* constants. By default, encryption is
* disabled. If the setting has an associated scheme for it, it will be
* used, and if not, the setting will be adjusted right after the
* connection is estabilished.
* @param resource $context A context for the socket.
*/
public function __construct(
$host,
$port,
$persist = false,
$timeout = null,
$key = '',
$crypto = parent::CRYPTO_OFF,
$context = null
) {
$this->streamType = '_CLIENT';
if (strpos($host, ':') !== false) {
$host = "[{$host}]";
}
$flags = STREAM_CLIENT_CONNECT;
if ($persist) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
$timeout
= null == $timeout ? ini_get('default_socket_timeout') : $timeout;
$key = rawurlencode($key);
if (null === $context) {
$context = stream_context_get_default();
} elseif ((!is_resource($context))
|| ('stream-context' !== get_resource_type($context))
) {
throw $this->createException('Invalid context supplied.', 6);
}
$hasCryptoScheme = array_key_exists($crypto, static::$cryptoScheme);
$scheme = $hasCryptoScheme ? static::$cryptoScheme[$crypto] : 'tcp';
$this->uri = "{$scheme}://{$host}:{$port}/{$key}";
set_error_handler(array($this, 'handleError'));
try {
parent::__construct(
stream_socket_client(
$this->uri,
$this->errorNo,
$this->errorStr,
$timeout,
$flags,
$context
)
);
restore_error_handler();
} catch (E $e) {
restore_error_handler();
if (0 === $this->errorNo) {
throw $this->createException(
'Failed to initialize socket.',
7,
$e
);
}
throw $this->createException(
'Failed to connect with socket.',
8,
$e
);
}
if ($hasCryptoScheme) {
$this->crypto = $crypto;
} elseif (parent::CRYPTO_OFF !== $crypto) {
$this->setCrypto($crypto);
}
$this->setIsBlocking(parent::CRYPTO_OFF === $crypto);
if ($persist) {
$this->shmHandler = SHM::factory(
__CLASS__ . ' ' . $this->uri . ' '
);
self::$lockState[$this->uri] = self::DIRECTION_NONE;
}
}
/**
* Creates a new exception.
*
* Creates a new exception. Used by the rest of the functions in this class.
*
* @param string $message The exception message.
* @param int $code The exception code.
* @param E|null $previous Previous exception thrown,
* or NULL if there is none.
* @param int|string|resource|null $fragment The fragment up until the
* point of failure.
* On failure with sending, this is the number of bytes sent
* successfully before the failure.
* On failure when receiving, this is a string/stream holding
* the contents received successfully before the failure.
*
* @return SocketException The exception to then be thrown.
*/
protected function createException(
$message,
$code = 0,
E $previous = null,
$fragment = null
) {
return new SocketException(
$message,
$code,
$previous,
$fragment,
$this->errorNo,
$this->errorStr
);
}
/**
* Locks transmission.
*
* Locks transmission in one or more directions. Useful when dealing with
* persistent connections. Note that every send/receive call implicitly
* calls this function and then restores it to the previous state. You only
* need to call this function if you need to do an uninterrputed sequence of
* such calls.
*
* @param int $direction The direction(s) to have locked. Acceptable values
* are the DIRECTION_* constants. If a lock for a direction can't be
* obtained immediatly, the function will block until one is aquired.
* Note that if you specify {@link DIRECTION_ALL}, the sending lock will
* be obtained before the receiving one, and if obtaining the receiving
* lock afterwards fails, the sending lock will be released too.
* @param bool $replace Whether to replace all locks with the specified
* ones. Setting this to FALSE will make the function only obtain the
* locks which are not already obtained.
*
* @return int|false The previous state or FALSE if the connection is not
* persistent or arguments are invalid.
*/
public function lock($direction = self::DIRECTION_ALL, $replace = false)
{
if ($this->persist && is_int($direction)) {
$old = self::$lockState[$this->uri];
if ($direction & self::DIRECTION_SEND) {
if (($old & self::DIRECTION_SEND)
|| $this->shmHandler->lock(self::DIRECTION_SEND)
) {
self::$lockState[$this->uri] |= self::DIRECTION_SEND;
} else {
throw new LockException('Unable to obtain sending lock.');
}
} elseif ($replace) {
if (!($old & self::DIRECTION_SEND)
|| $this->shmHandler->unlock(self::DIRECTION_SEND)
) {
self::$lockState[$this->uri] &= ~self::DIRECTION_SEND;
} else {
throw new LockException('Unable to release sending lock.');
}
}
try {
if ($direction & self::DIRECTION_RECEIVE) {
if (($old & self::DIRECTION_RECEIVE)
|| $this->shmHandler->lock(self::DIRECTION_RECEIVE)
) {
self::$lockState[$this->uri] |= self::DIRECTION_RECEIVE;
} else {
throw new LockException(
'Unable to obtain receiving lock.'
);
}
} elseif ($replace) {
if (!($old & self::DIRECTION_RECEIVE)
|| $this->shmHandler->unlock(self::DIRECTION_RECEIVE)
) {
self::$lockState[$this->uri]
&= ~self::DIRECTION_RECEIVE;
} else {
throw new LockException(
'Unable to release receiving lock.'
);
}
}
} catch (LockException $e) {
if ($direction & self::DIRECTION_SEND
&& !($old & self::DIRECTION_SEND)
) {
$this->shmHandler->unlock(self::DIRECTION_SEND);
}
throw $e;
}
return $old;
}
return false;
}
/**
* Sends a string or stream to the server.
*
* Sends a string or stream to the server. If a seekable stream is
* provided, it will be seeked back to the same position it was passed as,
* regardless of the $offset parameter.
*
* @param string|resource $contents The string or stream to send.
* @param int $offset The offset from which to start sending.
* If a stream is provided, and this is set to NULL, sending will start
* from the current stream position.
* @param int $length The maximum length to send. If omitted,
* the string/stream will be sent to its end.
*
* @return int The number of bytes sent.
*/
public function send($contents, $offset = null, $length = null)
{
if (false === ($previousState = $this->lock(self::DIRECTION_SEND))
&& $this->persist
) {
throw $this->createException(
'Unable to obtain sending lock',
10
);
}
try {
$result = parent::send($contents, $offset, $length);
} catch (E $e) {
$this->lock($previousState, true);
throw $e;
}
$this->lock($previousState, true);
return $result;
}
/**
* Receives data from the server.
*
* Receives data from the server as a string.
*
* @param int $length The number of bytes to receive.
* @param string $what Descriptive string about what is being received
* (used in exception messages).
*
* @return string The received content.
*/
public function receive($length, $what = 'data')
{
if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE))
&& $this->persist
) {
throw $this->createException(
'Unable to obtain receiving lock',
9
);
}
try {
$result = parent::receive($length, $what);
} catch (E $e) {
$this->lock($previousState, true);
throw $e;
}
$this->lock($previousState, true);
return $result;
}
/**
* Receives data from the server.
*
* Receives data from the server as a stream.
*
* @param int $length The number of bytes to receive.
* @param FilterCollection $filters A collection of filters to apply to the
* stream while receiving. Note that the filters will not be present on
* the stream after receiving is done.
* @param string $what Descriptive string about what is being
* received (used in exception messages).
*
* @return resource The received content.
*/
public function receiveStream(
$length,
FilterCollection $filters = null,
$what = 'stream data'
) {
if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE))
&& $this->persist
) {
throw $this->createException(
'Unable to obtain receiving lock',
9
);
}
try {
$result = parent::receiveStream($length, $filters, $what);
} catch (E $e) {
$this->lock($previousState, true);
throw $e;
}
$this->lock($previousState, true);
return $result;
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* Wrapper for network stream functionality.
*
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version 1.0.0a5
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;
use Exception as E;
/**
* A transmitter for connections to a socket server.
*
* This is a convinience wrapper for functionality of socket server connections.
* Used to ensure data integrity. Server handling is not part of the class in
* order to allow its usage as part of various server implementations (e.g. fork
* and/or sequential).
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <boen.robot@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class TcpServerConnection extends NetworkStream
{
/**
* @var string The IP address of the connected client.
*/
protected $peerIP;
/**
* @var int The port of the connected client.
*/
protected $peerPort;
/**
* Creates a new connection with the specified options.
*
* @param resource $server A socket server, created with
* {@link stream_socket_server()}.
* @param float $timeout The timeout for the connection.
*/
public function __construct($server, $timeout = null)
{
$this->streamType = '_SERVER';
if (!self::isStream($server)) {
throw $this->createException('Invalid server supplied.', 9);
}
$timeout
= null == $timeout ? ini_get('default_socket_timeout') : $timeout;
set_error_handler(array($this, 'handleError'));
try {
parent::__construct(
stream_socket_accept($server, $timeout, $peername)
);
restore_error_handler();
$portString = strrchr($peername, ':');
$this->peerPort = (int) substr($portString, 1);
$ipString = substr(
$peername,
0,
strlen($peername) - strlen($portString)
);
if (strpos($ipString, '[') === 0
&& strpos(strrev($ipString), ']') === 0
) {
$ipString = substr($ipString, 1, strlen($ipString) - 2);
}
$this->peerIP = $ipString;
} catch (E $e) {
restore_error_handler();
throw $this->createException(
'Failed to initialize connection.',
10,
$e
);
}
}
/**
* Gets the IP address of the connected client.
*
* @return string The IP address of the connected client.
*/
public function getPeerIP()
{
return $this->peerIP;
}
/**
* Gets the port of the connected client.
*
* @return int The port of the connected client.
*/
public function getPeerPort()
{
return $this->peerPort;
}
/**
* Creates a new exception.
*
* Creates a new exception. Used by the rest of the functions in this class.
*
* @param string $message The exception message.
* @param int $code The exception code.
* @param E|null $previous Previous exception thrown, or NULL if there
* is none.
* @param string|null $fragment The fragment up until the point of failure.
* NULL if the failure occured before the operation started.
*
* @return SocketException The exception to then be thrown.
*/
protected function createException(
$message,
$code = 0,
E $previous = null,
$fragment = null
) {
return new SocketException(
$message,
$code,
$previous,
$fragment
);
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class Paginator
{
public static function bootstrap($table, $w1='',$c1='', $w2='', $c2= '', $w3='',$c3='', $w4='', $c4= '', $per_page = '10')
{
global $routes;
global $_L;
$url = U.$routes['0'].'/'.$routes['1'].'/';
$adjacents = "2";
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
$pagination = "";
if($w1 != ''){
$totalReq = ORM::for_table($table)->where($w1,$c1)->count();
}elseif($w2 != ''){
$totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->count();
}elseif($w3 != ''){
$totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->where($w3,$c3)->count();
}elseif($w4 != ''){
$totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->where($w3,$c3)->where($w4,$c4)->count();
}else{
$totalReq = ORM::for_table($table)->count();
}
$i = 0;
$page = ($page == 0 ? 1 : $page);
$start = ($page - 1) * $per_page;
$prev = $page - 1;
$next = $page + 1;
$lastpage = ceil($totalReq / $per_page);
$lpm1 = $lastpage - 1;
$limit = $per_page;
$startpoint = ($page * $limit) - $limit;
if ($lastpage >= 1) {
$pagination .= '<ul class="pagination pagination-sm">';
if ($lastpage < 7 + ($adjacents * 2)) {
for ($counter = 1; $counter <= $lastpage; $counter++) {
if ($counter == $page)
$pagination .= "<li class='active'><a href='javascript:void(0);'>$counter</a></li>";
else
$pagination .= "<li><a href='{$url}$counter'>$counter</a></li>";
}
} elseif ($lastpage > 5 + ($adjacents * 2)) {
if ($page < 1 + ($adjacents * 2)) {
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) {
if ($counter == $page)
$pagination .= "<li class='active'><a href='javascript:void(0);'>$counter</a></li>";
else
$pagination .= "<li><a href='{$url}$counter'>$counter</a></li>";
}
$pagination .= "<li class='disabled'><a href='#'>...</a></li>";
$pagination .= "<li><a href='{$url}$lpm1'>$lpm1</a></li>";
$pagination .= "<li><a href='{$url}$lastpage'>$lastpage</a></li>";
} elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) {
$pagination .= "<li><a href='{$url}1'>1</a></li>";
$pagination .= "<li><a href='{$url}2'>2</a></li>";
$pagination .= "<li class='disabled'><a href='#'>...</a></li>";
for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) {
if ($counter == $page)
$pagination .= "<li class='active'><a href='javascript:void(0);'>$counter</a></li>";
else
$pagination .= "<li><a href='{$url}$counter'>$counter</a></li>";
}
$pagination .= "<li class='disabled'><a href='#'>...</a></li>";
$pagination .= "<li><a href='{$url}$lpm1'>$lpm1</a></li>";
$pagination .= "<li><a href='{$url}$lastpage'>$lastpage</a></li>";
} else {
$pagination .= "<li><a href='{$url}1'>1</a></li>";
$pagination .= "<li><a href='{$url}2'>2</a></li>";
$pagination .= "<li><a href='#'>...</a></li>";
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) {
if ($counter == $page)
$pagination .= "<li class='active'><a class='disabled'>$counter</a></li>";
else
$pagination .= "<li><a href='{$url}$counter'>$counter</a></li>";
}
}
}
if ($page < $counter - 1) {
$pagination .= "<li><a href='{$url}$next'>".$_L['Next']."</a></li>";
$pagination .= "<li><a href='{$url}$lastpage'>".$_L['Last']."</a></li>";
} else {
$pagination .= "<li class='disabled'><a class='disabled'>".$_L['Next']."</a></li>";
$pagination .= "<li class='disabled'><a class='disabled'>".$_L['Last']."</a></li>";
}
$pagination .= "</ul>";
$gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination);
return $gen;
}
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class Password{
public static function _crypt($password) {
return crypt($password);
}
public static function _verify($user_input, $hashed_password){
if (crypt($user_input, $hashed_password) == $hashed_password) {
return true;
}
return false;
}
public static function _uverify($user_input, $hashed_password){
if ($user_input == $hashed_password) {
return true;
}
return false;
}
public static function _gen(){
$pass = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#!123456789', 8)), 0, 8);
return $pass;
}
}

View File

@ -0,0 +1,16 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class Router{
public static function _info($name){
$d = ORM::for_table('tbl_routers')->where('name',$name)->find_one();
return $d;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
class Timezone {
public static function timezoneList()
{
$timezoneIdentifiers = DateTimeZone::listIdentifiers();
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
$tempTimezones = array();
foreach ($timezoneIdentifiers as $timezoneIdentifier) {
$currentTimezone = new DateTimeZone($timezoneIdentifier);
$tempTimezones[] = array(
'offset' => (int)$currentTimezone->getOffset($utcTime),
'identifier' => $timezoneIdentifier
);
}
// Sort the array by offset,identifier ascending
usort($tempTimezones, function($a, $b) {
return ($a['offset'] == $b['offset'])
? strcmp($a['identifier'], $b['identifier'])
: $a['offset'] - $b['offset'];
});
$timezoneList = array();
foreach ($tempTimezones as $tz) {
$sign = ($tz['offset'] > 0) ? '+' : '-';
$offset = gmdate('H:i', abs($tz['offset']));
$timezoneList[$tz['identifier']] = '(UTC ' . $sign . $offset . ') ' .
$tz['identifier'];
}
return $timezoneList;
}
}

22
system/autoload/User.php Normal file
View File

@ -0,0 +1,22 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
Class User{
public static function _info(){
$id = $_SESSION['uid'];
$d = ORM::for_table('tbl_customers')->find_one($id);
return $d;
}
public static function _billing(){
$id = $_SESSION['uid'];
$d = ORM::for_table('tbl_user_recharges')->where('customer_id',$id)->find_one();
return $d;
}
}

View File

@ -0,0 +1,284 @@
<?php
/**
* PHP Mikrotik Billing (www.phpmixbill.com)
* Ismail Marzuqi <iesien22@yahoo.com>
* @version 5.0
* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4
**/
/**
* Validator class
*/
class Validator{
/**
* String text finder
*
* @access private
* @param string $string
* @param array $hits
* @return void
*/
private static function textHit($string, $exclude=""){
if(empty($exclude)) return false;
if(is_array($exclude)){
foreach($exclude as $text){
if(strstr($string, $text)) return true;
}
}else{
if(strstr($string, $exclude)) return true;
}
return false;
}
/**
* Number compare
*
* @access private
* @param int $integer
* @param int $max
* @param int $min
* @return bool
*/
private static function numberBetween($integer, $max=null, $min=0){
if(is_numeric($min) && $integer <= $min) return false;
if(is_numeric($max) && $integer >= $max) return false;
return true;
}
/**
* Email addres check
*
* @access public
* @param string $string
* @param array $exclude
* @return bool
*/
public static function Email($string, $exclude=""){
if(self::textHit($string, $exclude)) return false;
return (bool)preg_match("/^([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\@([a-z0-9])(([a-z0-9-])*([a-z0-9]))+(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+$/i", $string);
}
/**
* URL check
*
* @access public
* @param strin $string
* @return bool
*/
public static function Url($string, $exclude=""){
if(self::textHit($string, $exclude)) return false;
return (bool)preg_match("/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $string);
}
/**
* IP
*
* @access public
* @param string $string
* @return void
*/
public static function Ip($string){
return (bool)preg_match("/^(1?\d{1,2}|2([0-4]\d|5[0-5]))(\.(1?\d{1,2}|2([0-4]\d|5[0-5]))){3}$/", $string);
}
/**
* Check if it is an number
*
* @access public
* @param int $integer
* @param int $max
* @param int $min
* @return bool
*/
public static function Number($integer, $max=null, $min=0){
if(preg_match("/^\-?\+?[0-9e1-9]+$/",$integer)){
if(!self::numberBetween($integer, $max, $min)) return false;
return true;
}
return false;
}
/**
* Check if it is an unsigned number
*
* @access public
* @param int $integer
* @return bool
*/
public static function UnsignedNumber($integer){
return (bool)preg_match("/^\+?[0-9]+$/",$integer);
}
/**
* Float
*
* @access public
* @param string $string
* @return bool
*/
public static function Float($string){
return (bool)($string==strval(floatval($string)))? true : false;
}
/**
* Alpha check
*
* @access public
* @param string $string
* @return void
*/
public static function Alpha($string){
return (bool)preg_match("/^[a-zA-Z]+$/", $string);
}
/**
* Alpha numeric check
*
* @access public
* @param string $string
* @return void
*/
public static function AlphaNumeric($string){
return (bool)preg_match("/^[0-9a-zA-Z]+$/", $string);
}
/**
* Specific chars check
*
* @access public
* @param string $string
* @param array $allowed
* @return void
*/
public static function Chars($string, $allowed=array("a-z")){
return (bool)preg_match("/^[" . implode("", $allowed) . "]+$/", $string);
}
/**
* Check length of an string
*
* @access public
* @param string $stirng
* @param int $max
* @param int $min
* @return bool
*/
public static function Length($string, $max=null, $min=0){
$length = strlen($string);
if(!self::numberBetween($length, $max, $min)) return false;
return true;
}
/**
* Hex color check
*
* @access public
* @param string $string
* @return void
*/
public static function HexColor($string){
return (bool)preg_match("/^(#)?([0-9a-f]{1,2}){3}$/i", $string);
}
/**
* Data validation
*
* Does'nt matter how you provide the date
* dd/mm/yyyy
* dd-mm-yyyy
* yyyy/mm/dd
* yyyy-mm-dd
*
* @access public
* @param string $string
* @return bool
*/
public static function Date($string){
$date = date('Y', strtotime($string));
return ($date == "1970" || $date == '') ? false : true;
}
/**
* Older than check
*
* @access public
* @param string $string
* @param int $age
* @return bool
*/
public static function OlderThan($string, $age){
$date = date('Y', strtotime($string));
if($date == "1970" || $date == '') return false;
return (date('Y') - $date) > $age ? true : false;
}
/**
* XML valid
*
* @access public
* @param string $string
* @return bool
*/
public static function Xml($string){
$Xml = @simplexml_load_string($string);
return ($Xml === false) ? false : true;
}
/**
* Is filesize between
*
* @access public
* @param string $file
* @param int $max
* @param int $min
* @return bool
*/
public static function FilesizeBetween($file, $max=null, $min=0){
$filesize = filesize($file);
return self::numberBetween($filesize, $max, $min);
}
/**
* Is image width between
*
* @access public
* @param string $image
* @param int $max_width
* @param int $min_width
* @param int $max_height
* @param int $min_height
* @return void
*/
public static function ImageSizeBetween($image, $max_width="", $min_width=0, $max_height="", $min_height=0){
$size = getimagesize($image);
if(!self::numberBetween($size[0], $max_width, $min_width)) return false;
if(!self::numberBetween($size[1], $max_height, $min_height)) return false;
return true;
}
/**
* Phone numbers
*
* @access public
* @param string $phone
* @return bool
*/
public static function Phone($phone){
$formats = array( '###-###-####',
'####-###-###',
'(###) ###-###',
'####-####-####',
'##-###-####-####',
'####-####',
'###-###-###',
'#####-###-###',
'##########',
'####-##-##-##');
$format = trim(preg_replace("/[0-9]/", "#", $phone));
return (bool)in_array($format, $formats);
}
}

View File

@ -0,0 +1,8 @@
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>