<?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.0b4
* @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 ACTION_EXIST = '';
/**
* Checks if the property does not exist.
*/
const ACTION_NOT_EXIST = '-';
/**
* Checks if the property equals a certain value.
*/
const ACTION_EQUALS = '=';
/**
* Checks if the property is less than a certain value.
*/
const ACTION_LESS_THAN = '<';
/**
* Checks if the property is greather than a certain value.
*/
const ACTION_GREATHER_THAN = '>';
/**
* @var array An array of the words forming the query. Each value is an
* array with the first member being the predicate (action 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 where()} to create an instance of it.
*/
private function __construct()
{
}
/**
* Sanitizes the action of a condition.
*
* @param string $action The action to sanitize.
*
* @return string The sanitized action.
*/
protected static function sanitizeAction($action)
{
$action = (string) $action;
switch ($action) {
case Query::ACTION_EXIST:
case Query::ACTION_NOT_EXIST:
case Query::ACTION_EQUALS:
case Query::ACTION_LESS_THAN:
case Query::ACTION_GREATHER_THAN:
return $action;
default:
throw new UnexpectedValueException(
'Unknown action specified',
UnexpectedValueException::CODE_ACTION_UNKNOWN,
null,
$action
);
}
}
/**
* Creates a new query with an initial condition.
*
* @param string $name The name of the property to test.
* @param string $value The value to test against. Not required for
* existence tests.
* @param string $action One of the ACTION_* constants. Describes the
* operation to perform.
*
* @return self|Query The query object.
*/
public static function where(
$name,
$value = null,
$action = self::ACTION_EXIST
) {
$query = new static;
return $query->addWhere($name, $value, $action);
}
/**
* Negates the query.
*
* @return self|Query 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 $value The value to test against. Not required for
* existence tests.
* @param string $action One of the ACTION_* constants. Describes the
* operation to perform.
*
* @return self|Query The query object.
*/
public function orWhere($name, $value = null, $action = self::ACTION_EXIST)
{
$this->addWhere($name, $value, $action)->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 $value The value to test against. Not required for
* existence tests.
* @param string $action One of the ACTION_* constants. Describes the
* operation to perform.
*
* @return self|Query The query object.
*/
public function andWhere($name, $value = null, $action = self::ACTION_EXIST)
{
$this->addWhere($name, $value, $action)->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_UNACCEPTING_QUERY
);
}
$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 $value The value to test against. Not required for
* existence tests.
* @param string $action One of the ACTION_* constants. Describes the
* operation to perform.
*
* @return self|Query The query object.
*/
protected function addWhere($name, $value, $action)
{
$this->words[] = array(
static::sanitizeAction($action)
. Message::sanitizeArgumentName($name),
(null === $value ? null : Message::sanitizeArgumentValue($value))
);
return $this;
}
}