<?php

namespace Swoft\Redis\Operator\ZSets;

class ZSetRangeByScore extends ZSetRange
{
    /**
     * [ZSet] zRevRangeByScore
     *
     * @return string
     */
    public function getId()
    {
        return 'zRangeByScore';
    }

    /**
     * {@inheritdoc}
     */
    protected function prepareOptions($options)
    {
        return [$options];
    }

    /**
     * {@inheritdoc}
     */
    protected function withScores()
    {
        $arguments = $this->getArguments();
        if (isset($arguments[3]) && is_array($arguments[3]) && array_key_exists('withscores', $arguments[3])) {
            return true;
        }

        return false;
    }
}


<?php

namespace Swoft\Redis\Operator\Processor;

use Swoft\Bean\Annotation\Inject;
use Swoft\Redis\Operator\CommandInterface;
use Swoft\Bean\Annotation\Bean;
use Swoft\Redis\Pool\Config\RedisPoolConfig;

/**
 * Class PrefixProcessor
 * @Bean()
 */
class PrefixProcessor implements ProcessorInterface
{
    /**
     * @Inject()
     * @var RedisPoolConfig
     */
    private $redisPoolConfig;

    /**
     * @var string
     */
    private $prefix;

    /**
     * @var array
     */
    private $commands;

    /**
     * Init
     */
    public function init()
    {
        $this->prefix = $this->redisPoolConfig->getPrefix();

        $this->commands = [
            'EXISTS'           => 'static::first',
            'LGETRANGE'        => 'static::first',
            'LGET'             => 'static::first',
            'DEL'              => 'static::all',
            'TYPE'             => 'static::first',
            'KEYS'             => 'static::first',
            'GETKEYS'          => 'static::first',
            'RENAME'           => 'static::all',
            'RENAMENX'         => 'static::all',
            'EXPIRE'           => 'static::first',
            'EXPIREAT'         => 'static::first',
            'TTL'              => 'static::first',
            'MOVE'             => 'static::first',
            'SORT'             => 'static::sort',
            'DUMP'             => 'static::first',
            'RESTORE'          => 'static::first',
            'SET'              => 'static::first',
            'SETNX'            => 'static::first',
            'MSET'             => 'static::interleaved',
            'MSETNX'           => 'static::interleaved',
            'GET'              => 'static::first',
            'MGET'             => 'static::all',
            'GETSET'           => 'static::first',
            'INCR'             => 'static::first',
            'INCRBY'           => 'static::first',
            'DECR'             => 'static::first',
            'DECRBY'           => 'static::first',
            'RPUSH'            => 'static::first',
            'LPUSH'            => 'static::first',
            'LLEN'             => 'static::first',
            'LRANGE'           => 'static::first',
            'LTRIM'            => 'static::first',
            'LINDEX'           => 'static::first',
            'LSET'             => 'static::first',
            'LREM'             => 'static::first',
            'LPOP'             => 'static::first',
            'RPOP'             => 'static::first',
            'RPOPLPUSH'        => 'static::all',
            'SADD'             => 'static::first',
            'SREM'             => 'static::first',
            'SREMOVE'          => 'static::first',
            'SPOP'             => 'static::first',
            'SMOVE'            => 'static::skipLast',
            'SCARD'            => 'static::first',
            'SSIZE'            => 'static::first',
            'SISMEMBER'        => 'static::first',
            'SCONTAINS'        => 'static::first',
            'SINTER'           => 'static::all',
            'SINTERSTORE'      => 'static::all',
            'SUNION'           => 'static::all',
            'SUNIONSTORE'      => 'static::all',
            'SDIFF'            => 'static::all',
            'SDIFFSTORE'       => 'static::all',
            'SMEMBERS'         => 'static::first',
            'SRANDMEMBER'      => 'static::first',
            'ZADD'             => 'static::first',
            'ZINCRBY'          => 'static::first',
            'ZREM'             => 'static::first',
            'ZDELETE'          => 'static::first',
            'ZRANGE'           => 'static::first',
            'ZREVRANGE'        => 'static::first',
            'ZRANGEBYSCORE'    => 'static::first',
            'ZCARD'            => 'static::first',
            'ZSCORE'           => 'static::first',
            'ZREMRANGEBYSCORE' => 'static::first',
            'SETEX'            => 'static::first',
            'APPEND'           => 'static::first',
            'SUBSTR'           => 'static::first',
            'BLPOP'            => 'static::skipLast',
            'BRPOP'            => 'static::skipLast',
            'ZCOUNT'           => 'static::first',
            'ZRANK'            => 'static::first',
            'ZREVRANK'         => 'static::first',
            'ZREMRANGEBYRANK'  => 'static::first',
            'HSET'             => 'static::first',
            'HSETNX'           => 'static::first',
            'HMSET'            => 'static::first',
            'HINCRBY'          => 'static::first',
            'HGET'             => 'static::first',
            'HMGET'            => 'static::first',
            'HDEL'             => 'static::first',
            'HEXISTS'          => 'static::first',
            'HLEN'             => 'static::first',
            'HKEYS'            => 'static::first',
            'HVALS'            => 'static::first',
            'HGETALL'          => 'static::first',
            'SUBSCRIBE'        => 'static::all',
            'PSUBSCRIBE'       => 'static::all',
            'PUBLISH'          => 'static::first',
            'PERSIST'          => 'static::first',
            'STRLEN'           => 'static::first',
            'SETRANGE'         => 'static::first',
            'GETRANGE'         => 'static::first',
            'SETBIT'           => 'static::first',
            'GETBIT'           => 'static::first',
            'RPUSHX'           => 'static::first',
            'LPUSHX'           => 'static::first',
            'LINSERT'          => 'static::first',
            'BRPOPLPUSH'       => 'static::skipLast',
            'ZREVRANGEBYSCORE' => 'static::first',
            'WATCH'            => 'static::all',
            'PTTL'             => 'static::first',
            'PEXPIRE'          => 'static::first',
            'PEXPIREAT'        => 'static::first',
            'PSETEX'           => 'static::first',
            'INCRBYFLOAT'      => 'static::first',
            'BITOP'            => 'static::skipFirst',
            'BITCOUNT'         => 'static::first',
            'HINCRBYFLOAT'     => 'static::first',
            'EVAL'             => 'static::evalKeys',
            'EVALSHA'          => 'static::evalKeys',
            'ZRANGEBYLEX'      => 'static::first',
            'ZREVRANGEBYLEX'   => 'static::first',
        ];
    }

    /**
     * Sets a prefix that is applied to all the keys.
     *
     * @param string $prefix Prefix for the keys.
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }

    /**
     * Gets the current prefix.
     *
     * @return string
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * {@inheritdoc}
     */
    public function process(CommandInterface $command)
    {
        if ($command instanceof PrefixableCommandInterface) {
            $command->prefixKeys($this->prefix);
        } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
            call_user_func($this->commands[$commandID], $command, $this->prefix);
        }
    }

    /**
     * Sets an handler for the specified command ID.
     *
     * The callback signature must have 2 parameters of the following types:
     *
     *   - Predis\Command\CommandInterface (command instance)
     *   - String (prefix)
     *
     * When the callback argument is omitted or NULL, the previously
     * associated handler for the specified command ID is removed.
     *
     * @param string $commandID The ID of the command to be handled.
     * @param mixed  $callback  A valid callable object or NULL.
     *
     * @throws \InvalidArgumentException
     */
    public function setCommandHandler($commandID, $callback = null)
    {
        $commandID = strtoupper($commandID);

        if (!isset($callback)) {
            unset($this->commands[$commandID]);

            return;
        }

        if (!is_callable($callback)) {
            throw new \InvalidArgumentException('Callback must be a valid callable object or NULL');
        }

        $this->commands[$commandID] = $callback;
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->getPrefix();
    }

    /**
     * Applies the specified prefix only the first argument.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function first(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $arguments[0] = "$prefix{$arguments[0]}";
            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to all the arguments.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function all(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            foreach ($arguments as &$key) {
                $key = "$prefix$key";
            }
            $arguments = [$arguments];

            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix only to even arguments in the list.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function interleaved(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $newArguments = [];
            foreach ($arguments as $key => $value) {
                $newKey = "{$prefix}{$key}";
                $newArguments[$newKey] = $value;
            }
            $arguments = [$newArguments];

            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to all the arguments but the first one.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function skipFirst(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $length = count($arguments);

            for ($i = 1; $i < $length; ++$i) {
                $arguments[$i] = "$prefix{$arguments[$i]}";
            }

            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to all the arguments but the last one.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function skipLast(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $length = count($arguments);

            for ($i = 0; $i < $length - 1; ++$i) {
                $arguments[$i] = "$prefix{$arguments[$i]}";
            }

            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to the keys of a SORT command.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function sort(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $arguments[0] = "$prefix{$arguments[0]}";

            if (($count = count($arguments)) > 1) {
                for ($i = 1; $i < $count; ++$i) {
                    switch (strtoupper($arguments[$i])) {
                        case 'BY':
                        case 'STORE':
                            $arguments[$i] = "$prefix{$arguments[++$i]}";
                            break;

                        case 'GET':
                            $value = $arguments[++$i];
                            if ($value !== '#') {
                                $arguments[$i] = "$prefix$value";
                            }
                            break;

                        case 'LIMIT';
                            $i += 2;
                            break;
                    }
                }
            }

            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to the keys of an EVAL-based command.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function evalKeys(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $command->setRawArguments($arguments);
        }
    }

    /**
     * Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE.
     *
     * @param CommandInterface $command Command instance.
     * @param string           $prefix  Prefix string.
     */
    public static function zsetStore(CommandInterface $command, $prefix)
    {
        if ($arguments = $command->getArguments()) {
            $arguments[0] = "$prefix{$arguments[0]}";
            $length = ((int)$arguments[1]) + 2;

            for ($i = 2; $i < $length; ++$i) {
                $arguments[$i] = "$prefix{$arguments[$i]}";
            }

            $command->setRawArguments($arguments);
        }
    }
}


<?php

namespace Swoft\Redis\Profile;

use Swoft\Redis\Exception\RedisException;
use Swoft\Redis\Operator\CommandInterface;
use Swoft\Redis\Operator\Processor\PrefixProcessor;

abstract class RedisProfile implements ProfileInterface
{
    /**
     * SupportedCommands
     *
     * @var array
     */
    private $commands;

    /**
     * @var PrefixProcessor
     */
    protected $processor;

    /**
     * Setter prefix
     * @param string $prefix
     */
    public function setPrefix(string $prefix)
    {
        $this->processor->setPrefix($prefix);
    }

    /**
     * Init
     */
    public function init()
    {
        $this->commands = $this->getSupportedCommands();
    }

    /**
     * {@inheritdoc}
     */
    public function supportsCommand(string $commandID): bool
    {
        return isset($this->commands[strtoupper($commandID)]);
    }

    /**
     * {@inheritdoc}
     */
    public function createCommand(string $commandID, array $arguments = []): CommandInterface
    {
        $commandID = strtoupper($commandID);

        if (!isset($this->commands[$commandID])) {
            throw new RedisException("Command '{$commandID}' is not a registered Redis command.");
        }

        $commandClass = $this->commands[$commandID];

        /* @var CommandInterface $command*/
        $command      = new $commandClass();
        $command->setArguments($arguments);

        if (isset($this->processor)) {
            $this->processor->process($command);
        }

        return $command;
    }

    /**
     * Setter PrefixProcessor
     */
    public function setProcessor($processor = null)
    {
        $this->processor = $processor;
    }

    /**
     * Getter PrefixProcessor
     */
    public function getProcessor()
    {
        return $this->processor;
    }

    /**
     * Returns a map of all the commands supported by the profile and their
     * actual PHP classes.
     *
     * @return array
     */
    abstract protected function getSupportedCommands(): array;
}


<?php
namespace sibilino\yii2\openlayers;

use yii\base\Widget;
use yii\helpers\Html;
use yii\helpers\Json;

/**
 * Yii 2 widget encapsulating OpenLayers 3 and offering simplified option specification.
 * @link https://github.com/Sibilino/yii2-openlayers
 * @copyright Copyright (c) 2015 Luis Hernández Hernández
 * @license http://opensource.org/licenses/MIT MIT
 */
class OpenLayers extends Widget
{
	/**
     * @var array the HTML attributes for the container div of this widget.
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
	public $options = [];
	/**
	 * The properties to be passed to the OpenLayers Map() constructor. In order to ease passing complex JavaScript structures, some simplifications are supported.
	 * See {@link OpenLayers::processMapOptions} for details on simplified option specification.
	 * @var array
	 */
	public $mapOptions = [];
    /**
     * The scripts that operate with the olwidget.js module, e. g. to apply map configuration in plain JavaScript.
     * Can be array to register multiple scripts. If the array is given string keys, they will be passed to [[yii\web\View::registerJsFile()]].
     * @var string|array
     */
    public $mapOptionScript = [];

	public function init()
	{
		if (!isset($this->options['id'])) {
			$this->options['id'] = $this->getId();
		}
		$this->mapOptions['target'] = $this->options['id'];
		OpenLayersBundle::register($this->view);
        OLModuleBundle::register($this->view);
	}

	public function run()
	{
		$this->processMapOptions();

        $scripts = is_array($this->mapOptionScript) ? $this->mapOptionScript : [$this->mapOptionScript];
        foreach ($scripts as $key => $script) {
            if (!is_string($key)) {
                $key = null; // Dont specify a key for non-associative array of scripts
            }
            $this->view->registerJsFile($script, ['depends'=>OLModuleBundle::className()], $key);
        }

        $script = 'sibilino.olwidget.createMap('.Json::encode($this->mapOptions).', "'.$this->options['id'].'")';
		$this->view->registerJs($script);

		return Html::tag('div', '', $this->options);
	}

	/**
	 * Checks whether several "complex" options have been specified as a key => value pair where key is a string.
	 * If found, those properies will be automatically turned into the JavaScript expression that instantiates the corresponding OpenLayers object, thus eliminating the need to manually create a JsExpression object.
	 * The value for these processed keys will be passed as options to the constructor of the OpenLayers object.
	 * Supported simplified properties are:
	 *

	 *
view: Can be specified as 'view' => $optionArray.
	 * For example, ['view' => ['center'=>[0,0],'zoom'=>2]] will produce this JS: new ol.View({"center":[0,0], "zoom":2})
	 *
layers: Each layer in the array can be specified as $type => $options, where $type is a string.
	 * For example, ['layers' => ['Tile' => ['visible' => false] will produce this JS: new ol.layer.Tile({"visible": false})
	 *
layer sources: In addition, when a layer's $type => $option pair are both strings, the $option is considered the layer's "source", and will also be converted to the corresponding object.
	 * For example, ['layers' => ['Tile' => 'OSM'] will produce this JS: new ol.layer.Tile({"source": new ol.source.OSM()})
	 *

	 *

	 * If these simplifications are not enough to avoid using complex JsExpression structures, make sure to see the {@link OL} class for an abbreviated way of specifying OpenLayers object instances.
	 * @see OL
	 */
	protected function processMapOptions()
	{
		if (isset($this->mapOptions['view']) && is_array($this->mapOptions['view'])) {
			$this->mapOptions['view'] = new OL('View', $this->mapOptions['view']);
		}

		if (isset($this->mapOptions['layers'])) {
			$this->processSimplifiedLayers();
		}
	}

	/**
	 * Takes each key => value pair in $this->mapOptions['layers'] and checks if key is a string. In that case, a new Layer OL object will created for this layer.
	 * Then, if the value is also a string, the corresponding Source OL object is created as well.
	 */
	protected function processSimplifiedLayers()
	{
		$processedLayers = [];
		foreach ($this->mapOptions['layers'] as $type => $options) {
			if (is_string($type)) {
				if (is_string($options)) {
                    $options = ['source' => new OL("source.$options")];
                }
				$processedLayers []= new OL("layer.$type", $options);
			} else { // Therefore $type is simply an integer array key
                $processedLayers []= $options;
            }
		}
		$this->mapOptions['layers'] = $processedLayers;
	}
}


<?php
namespace sibilino\yii2\openlayers;

use yii\web\JsExpression;
use yii\helpers\Json;

/**
 * This class makes it easier to specify OpenLayers objects as options for the OpenLayers widget, resulting in more concise PHP array structures than when using JsExpression.
 * See {@link OL::__construct} for usage examples.
 * @link https://github.com/Sibilino/yii2-openlayers
 * @copyright Copyright (c) 2015 Luis Hernández Hernández
 * @license http://opensource.org/licenses/MIT MIT
 */
class OL extends JsExpression
{
	/**
	 * Must include the full namespace path, excluding 'ol.'. For example, "layer.Tile".
	 * @var string the classname to be instantiated.
	 */
	protected $class;
	/**
	 * @var array|null the properties to be passed to the constructor of $class.
	 */
	protected $properties;

	/**
	 * The OL class takes a classname and an optional property array as constructor arguments. Then, when an OL object is passed to Json::encode(), the following expression will be generated:
	 * new ol.$class($properties).
	 * Therefore, the need for JsExpression is eliminated, and the 'ol.' namespace, the 'new' keyword and constructor call are automatically added.
	 * For example, the following mapOptions array:
	 *
//...
	 * 'layers' => [
	 *   new OL('layer.Tile', [
	 *		'source' => new OL('source.MapQuest', [
	 *			'layer' => 'sat',
	 *		]),
	 *	 ]),
	 * ],
	 * 'view' => [
	 * 	 new OL('View', [
	 * 		'center' => [0, 0],
	 * 		'zoom' => 4,
	 * 	],
	 * ]
	 * /...

	 * Will be encoded as follows:
	 *

	 * {
	 * 	"layers": [
	 * 		new ol.layer.Tile({
	 * 			"source": new ol.source.MapQuest({
	 * 				"layer": "sat"
	 * 			})
	 * 		})
	 * 	],
	 * 	"view": new ol.View({
	 * 		"center": [0, 0],
	 * 		"zoom": 4
	 * 	})
	 * }
	 *

	 * @param string $class
	 * @param mixed $properties
	 */
	public function __construct($class, $properties = null)
	{
		$this->class = $class;
		$this->properties = $properties;
		$this->expression = $this->generateExpression();
	}

	/**
	 * Generates the JavaScript expression that correspond with this object's $class and $properties.
	 * @return string
	 */
	protected function generateExpression()
	{
		if (isset($this->properties))
			$properties = Json::encode($this->properties);
		else
			$properties = '';
		return "new ol.$this->class($properties)";
	}

    public function __toString() {
        return $this->expression;
    }

}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2012
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * Socket class in OrientDB-PHP library.
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBSocket
{
    /**
     * Socket object
     * @var resource
     */
    private $socket;

    /**
     * Default buffer length
     * @var int
     */
    private $bufferLen;

    /**
     * Control debug output
     * @var bool
     */
    public $debug = false;

    /**
     * Create new instance
     * @param string $host
     * @param int $port
     * @param int $timeout
     * @param int $bufferLen
     * @throws OrientDBConnectException
     */
    public function __construct($host, $port, $timeout = 30, $bufferLen = 16384)
    {
        $this->socket = @fsockopen($host, $port, $errno, $errstr, $timeout);

        if ($this->socket === false) {
            throw new OrientDBConnectException("Socket error #{$errno}: {$errstr}");
        }

        stream_set_blocking($this->socket, 1);
        stream_set_timeout($this->socket, 1);

        $this->bufferLen = $bufferLen;
    }

    /**
     * Destructor
     */
    public function __destruct()
    {
        fclose($this->socket);
    }

    /**
     * Read bytes from socket
     * @param int $length Bytes to read
     * @return string
     */
    public function read($length = null)
    {
        $data = fread($this->socket, $length === null ? $this->bufferLen : $length);
        if ($this->debug) {
            OrientDBHelpers::hexDump($data);
        }
        return $data;
    }

    /**
     * Send data to socket
     * @param string $data
     */
    public function send($data)
    {
        fwrite($this->socket, $data);
        if ($this->debug) {
            OrientDBHelpers::hexDump($data);
        }
    }

    /**
     * Check if socket still a valid resource
     * @return bool
     */
    public function isValid()
    {
        return is_resource($this->socket);
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2013
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * recordLoad() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandRecordLoad extends OrientDBCommandAbstract
{

    /**
     * ClusterID
     * @var int
     */
    protected $clusterID;

    /**
     * Record position
     * @var int
     */
    protected $recordPos;

    /**
     * Fetchplan
     * @var string
     */
    protected $fetchPlan;

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::RECORD_LOAD;
    }

    public function prepare()
    {
        parent::prepare();
        if (count($this->attribs) > 2 || count($this->attribs) < 1) {
            throw new OrientDBWrongParamsException('This command requires record ID and, optionally, fetch plan');
        }
        $arr = explode(':', $this->attribs[0]);
        if (count($arr) != 2) {
            throw new OrientDBWrongParamsException('Wrong format for record ID');
        }
        $this->clusterID = (int) $arr[0];
        $this->recordPos = (int) $arr[1];

        if ((string) $this->clusterID !== $arr[0] || (string) $this->recordPos !== $arr[1]) {
            throw new OrientDBWrongParamsException('Wrong format for record ID');
        }
        // Add ClusterID
        $this->addShort($this->clusterID);
        // Add RecordID
        $this->addLong($this->recordPos);
        // Fetchplan
        $this->fetchPlan = '*:0';
        if (count($this->attribs) == 2) {
            $this->fetchPlan = $this->attribs[1];
        }
        $this->addString($this->fetchPlan);
        // Cache ignore - use cache
        $this->addByte(0);
        // Flag - Tombstone
        if ($this->parent->protocolVersion >= 13) {
            $this->addByte(0);
        }
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return bool|OrientDBTypeLink|OrientDBRecord
     */
    protected function parseResponse()
    {
        $this->debugCommand('record_status_first');
        $status = $this->readByte();
        if ($status != chr(0)) {
            $this->debugCommand('record_content');
            $record_content = $this->readBytes();
            $this->debugCommand('record_version');
            $record_version = $this->readInt();
            $this->debugCommand('record_type');
            $record_type = $this->readByte();
            $this->debugCommand('record_status_cache');
            $status = $this->readByte();

            $cachedRecords = array();
            while ($status != chr(0)) {
                $this->debugCommand('record_content');
                $record = $this->readRecord();
                $cachedRecords[$record->recordID] = $record;
                $this->debugCommand('record_status_next');
                $status = $this->readByte();
            }
            // Invalidate cache
            $this->parent->cachedRecords = $cachedRecords;
            // Form a record
            $record = new OrientDBRecord();
            $record->type = $record_type;
            $record->clusterID = $this->clusterID;
            $record->recordPos = $this->recordPos;
            $record->version = $record_version;
            $record->content = $record_content;

            return $record;
        }
        return false;
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2013
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * Main class in OrientDB-PHP library.
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 *
 * @method mixed command() command(int $commandMode, string $query, string $fetchplan = null) Execute SQL-like command
 * @method void commit() commit() Not yet implemented
 * @method array configList() configList() Return list of server config options
 * @method string configGet() configGet(string $optionName) Get value of an option
 * @method boolean configSet() configSet(string $optionName, string $optionValue) Set value for config option
 * @method boolean connect() connect(string $userName, string $password) Connect to OrientDB server
 * @method int count() count(string $clusterName)  Count elements in cluster
 * @method int dataclusterAdd() dataclusterAdd(string $clusterName, string $clusterType) Add a datacluster to DB
 * @method boolean dataclusterRemove() dataclusterRemove(int $clusterID) Remove datacluster from DB
 * @method int dataclusterCount() dataclusterCount(array $clusterIDs) Count elements in clusters
 * @method array dataclusterDatarange() dataclusterDatarange(int $clusterID) Return datarange for datacluster
 * @method int datasegmentAdd() datasegmentAdd(string $name, string $location) Not yet implemented. Create new datasegment
 * @method boolean datasegmentDelete() datasegmentDelete(string $name) Not yet implemented. Drop datasegment by name.
 * @method array DBOpen() DBOpen(string $dbName, string $userName, string $password) Open OrientDB database
 * @method void DBClose() DBClose() Closes currently opened DB
 * @method boolean DBCreate() DBCreate(string $dbName, string $dbType) Create new database
 * @method boolean DBDelete() DBDelete(string $dbName) Delete DB
 * @method boolean DBExists() DBExists(string $dbName) Check if DB exists
 * @method array DBList() DBList() Return DB list
 * @method mixed query(string $query) Execute general style query, for SELECT query see select() method
 * @method int recordCreate() recordCreate(int $clusterID, string $recordContent, string $recordType = OrientDB::RECORD_TYPE_DOCUMENT) Create a new record
 * @method boolean recordDelete() recordDelete(string $recordID, int $recordVersion = -1) Delete a record
 * @method OrientDBRecord recordLoad() recordLoad(string $recordID, string $fetchPlan = null) Load a record
 * @method int recordUpdate() recordUpdate(string $recordID, string $recordContent, int $recordVersion = -1, string $recordType = OrientDB::RECORD_TYPE_DOCUMENT) Update a record
 * @method mixed select() select(string $query) Execute sync-style select query
 * @method mixed selectAsync() selectAsync(string $query, string $fetchplan = null) Execute async-style select query with optional fetchplan
 * @method mixed selectGremlin() selectGremlin(string $query, string $fetchplan = null) Execute Gremlin-style select query. Only for OrientDB - Graph Edition
 * @method void shutdown() shutdown(string $userName, string $password) Shutdown OrientDB server remotely
 */
class OrientDB
{

    /**
     * Hostname of OrientDB server
     * @var string
     */
    private $host;

    /**
     * Port of OrientDB server
     * @var int
     */
    private $port;

    /**
     * Socked, connected to OrientDB server
     * @var OrientDBSocket
     */
    public $socket;

    /**
     * Debug information output control
     * @var bool
     */
    private $debug = false;

    /**
     * Client protocol version
     * @var int
     */
    public $clientVersion = 15;

    /**
     * Server's protocol version.
     * @var int
     */
    public $protocolVersion = null;

    /**
     * If we connected() to server
     * @var bool
     */
    protected $connected = false;

    /**
     * If we DBOpen() some DB
     * @var bool
     */
    protected $DBOpen = false;

    /**
     * If no DBClose() called
     * @var bool
     */
    protected $active = true;

    /**
     * SessionID returned from OrientDB, used to further identify queries to server
     * @var int
     */
    private $sessionIDServer;

    /**
     * SessionID returned from OrientDB, used to further identify queries to DB
     * @var int
     */
    private $sessionIDDB;

    const DRIVER_NAME = 'OrientDB-PHP';

    const DRIVER_VERSION = 'beta-0.4.9';

    /**
     * Record type Bytes
     * @var string
     */
    const RECORD_TYPE_BYTES = 'b';

    /**
     * Record type Document
     * @var string
     */
    const RECORD_TYPE_DOCUMENT = 'd';

    /**
     * Record type Flat
     * @var string
     */
    const RECORD_TYPE_FLAT = 'f';

    /**
     * List of available record types
     * @var array
     */
    public static $recordTypes = array(
        self::RECORD_TYPE_BYTES,
        self::RECORD_TYPE_DOCUMENT,
        self::RECORD_TYPE_FLAT);

    /**
     * Database type memory
     * @var string
     */
    const DB_TYPE_MEMORY = 'memory';

    /**
     * Database type local (disk)
     * @var string
     */
    const DB_TYPE_LOCAL = 'local';

    /**
     * Query type general
     * @var int
     */
    const COMMAND_QUERY = 1;

    /**
     * Query type synchronous select
     * @var int
     */
    const COMMAND_SELECT_SYNC = 2;

    /**
     * Query type asynchronous select
     * @var int
     */
    const COMMAND_SELECT_ASYNC = 3;

    /**
     * Query type asynchronous select
     * @var int
     */
    const COMMAND_SELECT_GREMLIN = 4;

    /**
     * Datacluster type physical (disk)
     * @var string
     */
    const DATACLUSTER_TYPE_PHYSICAL = 'PHYSICAL';

    /**
     * Datacluster type memory
     * @var string
     */
    const DATACLUSTER_TYPE_MEMORY = 'MEMORY';

    /**
     * List of available datacluster types
     * @var array
     */
    public static $clusterTypes = array(
        self::DATACLUSTER_TYPE_PHYSICAL,
        self::DATACLUSTER_TYPE_MEMORY);

    /**
     * Associative list of cached records, if any
     * @var array
     */
    public $cachedRecords = array();

    /**
     * List of commands, requires to be connect()ed first
     * @var array
     */
    private static $requireConnect = array(
        OrientDBCommandAbstract::SHUTDOWN,
        OrientDBCommandAbstract::DB_CREATE,
        OrientDBCommandAbstract::DB_DELETE,
        OrientDBCommandAbstract::DB_EXIST,
        OrientDBCommandAbstract::CONFIG_GET,
        OrientDBCommandAbstract::CONFIG_SET,
        OrientDBCommandAbstract::CONFIG_LIST,
        OrientDBCommandAbstract::DB_LIST);

    /**
     * List of commands, requires to be DBOpen()ed first
     * @var array
     */
    private static $requireDBOpen = array(
        OrientDBCommandAbstract::DB_CLOSE,
        OrientDBCommandAbstract::DATACLUSTER_ADD,
        OrientDBCommandAbstract::DATACLUSTER_REMOVE,
        OrientDBCommandAbstract::DATACLUSTER_COUNT,
        OrientDBCommandAbstract::DATACLUSTER_DATARANGE,
        OrientDBCommandAbstract::DATASEGMENT_ADD,
        OrientDBCommandAbstract::DATASEGMENT_REMOVE,
        OrientDBCommandAbstract::RECORD_LOAD,
        OrientDBCommandAbstract::RECORD_CREATE,
        OrientDBCommandAbstract::RECORD_UPDATE,
        OrientDBCommandAbstract::RECORD_DELETE,
        OrientDBCommandAbstract::COUNT,
        OrientDBCommandAbstract::COMMAND,
        OrientDBCommandAbstract::TX_COMMIT);

    /**
     * Construct new instance of OrientDB-PHP
     * @param string $host
     * @param int $port
     * @param int $timeout
     */
    public function __construct($host, $port = 2424, $timeout = 30)
    {
        $this->host = $host;
        $this->port = $port;
        $this->socket = new OrientDBSocket($host, $port, $timeout);
    }

    /**
     * Destructor, implicitly unset $this->socket
     */
    public function __destruct()
    {
        if ($this->isDBOpen() && $this->socket->isValid()) {
            $this->DBClose();
        }
    }

    /**
     * If connect() called on class instance
     * @return bool
     */
    public function isConnected()
    {
        return $this->connected;
    }

    /**
     * If DBOpen() called on class instance
     * @return bool
     */
    public function isDBOpen()
    {
        return $this->DBOpen;
    }

    /**
     * Main magic method
     * @param string $name
     * @param array $arguments
     * @throws OrientDBWrongCommandException
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $className = 'OrientDBCommand' . ucfirst($name);
        if (class_exists($className)) {
            /**
             * @var $command OrientDBCommandAbstract
             */
            $command = new $className($this);
            $this->canExecute($command);
            call_user_func_array(array(
                $command,
                'setAttribs'), $arguments);

            $command->prepare();
            $data = $command->execute();

            if ($command->opType == OrientDBCommandAbstract::CONNECT) {
                $this->connected = true;
            }
            if ($command->opType == OrientDBCommandAbstract::DB_OPEN) {
                $this->DBOpen = true;
            }
            if ($command->opType == OrientDBCommandAbstract::DB_CLOSE) {
                $this->DBOpen = false;
                $this->active = false;
                $this->socket = null;
            }
            return $data;
        } else {
            throw new OrientDBWrongCommandException('Command ' . $className . ' currently not implemented');
        }
    }

    /**
     * Check if command's requirements are met
     * @param OrientDBCommandAbstract $command Command instance
     * @throws OrientDBWrongCommandException
     * @see OrientDBCommandAbstract
     */
    protected function canExecute($command)
    {

        if (!$this->active) {
            throw new OrientDBWrongCommandException('DBClose was executed. No interaction is possible.');
        }
        if (in_array($command->opType, $this->getCommandsRequiresConnect()) && !$this->isConnected()) {
            throw new OrientDBWrongCommandException('Not connected to server');
        }
        if (in_array($command->opType, $this->getCommandsRequiresDBOpen()) && !$this->isDBOpen()) {
            throw new OrientDBWrongCommandException('Database not open');
        }
    }

    /**
     * Control debug output status
     * @param bool $debug
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
    }

    /**
     * Return debug output status
     * @return bool
     */
    public function isDebug()
    {
        return $this->debug;
    }

    /**
     * Check if current client protocol version matches server version
     * @param int $version Server version
     * @throws OrientDBException
     */
    public function setProtocolVersion($version)
    {
        $this->protocolVersion = $version;
        if ($this->protocolVersion < $this->clientVersion) {
            throw new OrientDBException('Binary protocol is uncompatible with the Server connected: clientVersion=' . $this->clientVersion . ', serverVersion=' . $this->protocolVersion);
        }
    }

    /**
     * Return this driver supported protocol version
     * @return int
     */
    public function getProtocolVersionClient()
    {
        return $this->clientVersion;
    }

    /**
     * Returns list of commands require to previously run connect()
     * @return array
     */
    public function getCommandsRequiresConnect()
    {
        return self::$requireConnect;
    }

    /**
     * Returns list of commands require to previously run DBOpen()
     * @return array
     */
    public function getCommandsRequiresDBOpen()
    {
        return self::$requireDBOpen;
    }

    /**
     * Set sessionID for use with server queries
     * @param int $sessionID
     */
    public function setSessionIDServer($sessionID)
    {
        $this->sessionIDServer = $sessionID;
    }

    /**
     * Set sessionID for use with DB queries
     * @param int $sessionID
     */
    public function setSessionIDDB($sessionID)
    {
        $this->sessionIDDB = $sessionID;
    }

    /**
     * Return sessionID for server queries
     * @return int
     */
    public function getSessionIDServer()
    {
        return $this->sessionIDServer;
    }

    /**
     * Return sessionID for DB queries
     * @return int
     */
    public function getSessionIDDB()
    {
        return $this->sessionIDDB;
    }
}

/**
 * Base exception for OrientDB-PHP
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBException extends Exception
{
}

/**
 * Exception for connection problems
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBConnectException extends OrientDBException
{
}

/**
 * Exception for wrong command sequence
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBWrongCommandException extends OrientDBException
{
}

/**
 * Exception for wrong command's param
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBWrongParamsException extends OrientDBException
{
}

/**
 * Exception for de-serialization errors
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Main
 */
class OrientDBDeSerializeException extends OrientDBException
{
}

if (!function_exists('OrientDB_autoload')) {

    /**
     *
     * Default autoload function for OrientDB-PHP
     * @package OrientDB-PHP
     * @param string $className
     */
    function OrientDB_autoload($className)
    {
        $prefix = 'OrientDB';
        if (strpos($className, $prefix) === 0) {
            $classTokens = substr($className, strlen($prefix));
            preg_match_all('/[A-Z]+[^A-Z]+/', $classTokens, $classTokens);
            $classToken = reset($classTokens[0]);

            switch ($classToken) {
                case 'Command':
                    $fileName = 'Commands/' . $className . '.php';
                    break;
                case 'Type':
                    $fileName = 'OrientDBDataTypes.php';
                    break;
                default:
                    $fileName = $className . '.php';
                    break;
            }
            $fullName = dirname(__FILE__) . '/' . $fileName;
            if (file_exists($fullName)) {
                require_once $fullName;
            }
        }
    }

    spl_autoload_register('OrientDB_autoload');
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * recordDelete() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandRecordDelete extends OrientDBCommandAbstract
{

    /**
     * ClusterID
     * @var int
     */
    protected $clusterID;

    /**
     * Record position
     * @var int
     */
    protected $recordPos;

    /**
     * Record type
     * @var string
     * @see OrientDB::$recordTypes
     */
    protected $recordType;

    /**
     * Record version
     * @var int
     */
    protected $version;

    /**
     * Mode. 0 = synchronous (default mode waits for the answer) 1 = asynchronous (don't need an answer)
     * @var int
     */
    protected $mode = 0x00;

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::RECORD_DELETE;
    }

    public function prepare()
    {
        parent::prepare();
        if (count($this->attribs) > 2 || count($this->attribs) < 1) {
            throw new OrientDBWrongParamsException('This command requires record ID and, optionally, record version');
        }
        $arr = explode(':', $this->attribs[0]);
        if (count($arr) != 2) {
            throw new OrientDBWrongParamsException('Wrong format for record ID');
        }
        $this->clusterID = (int) $arr[0];
        $this->recordPos = (int) $arr[1];

        if ($this->clusterID === 0 || (string) $this->recordPos !== $arr[1]) {
            throw new OrientDBWrongParamsException('Wrong format for record ID');
        }
        if (count($this->attribs) == 2) {
            $this->version = (int) $this->attribs[1];
        } else {
            // Pessimistic way
            $this->version = -1;
        }
        // Add ClusterID
        $this->addShort($this->clusterID);
        // Add RecordID
        $this->addLong($this->recordPos);
        // Add version
        $this->addInt($this->version);
        // Synchronous mode
        $this->addByte(chr($this->mode));
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return bool
     */
    protected function parseResponse()
    {
        $this->debugCommand('delete_result');
        $result = $this->readByte();
        if ($result == chr(1)) {
            return true;
        } else {
            return false;
        }
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2013
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * Class to encode OrientDB record
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Datatypes
 */
class OrientDBRecordEncoder
{

    /**
     * ClassName as parsed from document
     * @var string
     */
    protected $className;

    /**
     * A placeholder for document data
     * @var StdClass
     */
    protected $data;

    /**
     * Serialized record
     * @var string
     */
    public $buffer;

    /**
     * Construct new instance
     * @param OrientDBData $data Data from OrientDBRecord instance
     * @param string $className ClassName from OrientDBRecord instance
     * @return \OrientDBRecordEncoder
     */
    public function __construct($data, $className = null)
    {
        $this->data = $data;
        $this->className = $className;
        $this->encode();
    }

    /**
     * Parses $this->content and populates $this->data and $this->className
     * @return void
     */
    protected function encode()
    {
        if (!is_null($this->className)) {
            $this->buffer .= $this->className . '@';
        }
        $tokens = $this->process($this->data);

        $this->buffer .= implode(',', $tokens);
    }

    /**
     * Recursively encodes data to OrientDB representation
     * @param array|OrientDBData $data Data to be encoded
     * @param bool $isAssoc Is keys needs to be included
     * @param bool $isArray Is this array or class
     * @throws OrientDBException
     * @return array Array of tokens
     */
    protected function process($data, $isAssoc = true, $isArray = false)
    {
        $tokens = array();

        foreach ($data as $key => $value) {
            $buffer = '';
            if ($isAssoc) {
                if ($isArray) {
                    $buffer = self::encodeString($key) . ':';
                } else {
                    $buffer = $key . ':';
                }
            }
            /**
             *
             * PHP manual says what gettype() is slow and we should use is_* functions instead. But tests says that its approx. 5-10% faster encoding using single gettype() call instead of separate calls to is_int(), is_string(), etc.
             * @var string
             */
            $valueType = gettype($value);
            switch ($valueType) {
                case 'integer':
                    $buffer .= $value;
                    break;

                case 'double':
                    $buffer .= $value . chr(OrientDBRecordDecoder::CCODE_NUM_FLOAT);
                    break;

                case 'string':
                    $buffer .= self::encodeString($value);
                    break;

                case 'boolean':
                    if ($value === true) {
                        $buffer .= 'true';
                    } else {
                        $buffer .= 'false';
                    }
                    break;

                case 'array':
                    $arrayAssoc = self::isAssoc($value);
                    if ($arrayAssoc === true) {
//                        This is assoc array, using map
                        $boundStart = chr(OrientDBRecordDecoder::CCODE_OPEN_CURLY);
                        $boundEnd = chr(OrientDBRecordDecoder::CCODE_CLOSE_CURLY);
                    } else {
//                        Array will always be encoded as a set, as 1) Orient itself converts set to list (and vice-versa too) 2) PHP lacks build-in set type
                        $boundStart = chr(OrientDBRecordDecoder::CCODE_OPEN_SQUARE);
                        $boundEnd = chr(OrientDBRecordDecoder::CCODE_CLOSE_SQUARE);
                    }
                    $buffer .= $boundStart;
                    /**
                     * @TODO Fix possible PHP fatal: Maximum function nesting level of '100' reached, aborting!
                     */
                    $buffer .= implode(',', $this->process($value, $arrayAssoc, true));
                    $buffer .= $boundEnd;
                    break;

                case 'NULL':

                    break;

                case is_a($value, 'OrientDBTypeLink'):
                    /** @var $value OrientDBTypeLink */
                    $buffer .= $value->getHash();
                    break;

                case is_a($value, 'OrientDBTypeDate'):
                    $buffer .= (string) $value;
                    break;

                case is_a($value, 'OrientDBRecord'):
                    $buffer .= chr(OrientDBRecordDecoder::CCODE_OPEN_PARENTHESES);
                    /** @var $value OrientDBRecord */
                    $buffer .= $value->__toString();
                    $buffer .= chr(OrientDBRecordDecoder::CCODE_CLOSE_PARENTHESES);
                    break;

                default:
                    throw new OrientDBException('Can\'t serialize: ' . $valueType);
            }
            $tokens[] = $buffer;
        }
        return $tokens;
    }

    /**
     * Returns escaped string, suitable for OrientDBRecord format
     * @param string $string
     * @return string
     */
    public static function encodeString($string)
    {
        /**
         * @TODO Unit tests
         */
        return '"' . addcslashes($string, '"\\') . '"';
    }

    /**
     * Check is array is associative or sequential
     * @param array $array
     * @return bool
     */
    protected static function isAssoc($array)
    {
        if (!is_array($array)) {
            return null;
        }
        return (bool) count(array_filter(array_keys($array), 'is_string'));
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2012
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * recordCreate() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandRecordCreate extends OrientDBCommandAbstract
{

    /**
     * ClusterID
     * @var int
     */
    protected $clusterID;

    /**
     * Record type
     * @var string
     * @see OrientDB::$recordTypes
     */
    protected $recordType;

    /**
     * Record content
     * @var string|OrientDBRecord
     */
    protected $recordContent;

    /**
     * Mode. 0 = synchronous (default mode waits for the answer) 1 = asynchronous (don't need an answer)
     * @var int
     */
    protected $mode = 0x00;

    /**
     * @var int
     */
    protected $datasegmentID = 0;

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::RECORD_CREATE;
    }

    public function prepare()
    {
        parent::prepare();
        if (count($this->attribs) > 3 || count($this->attribs) < 2) {
            throw new OrientDBWrongParamsException('This command requires cluster ID, record content and, optionally, record Type');
        }
        // Add datasegment ID
        $this->addInt($this->datasegmentID);
        // Process clusterID
        $this->clusterID = (int) $this->attribs[0];
        // Add ClusterID
        $this->addShort($this->clusterID);
        // Process recordContent
        $this->recordContent = $this->attribs[1];
        // Add RecordContent
        $this->addBytes($this->recordContent);
        // recordType
        $this->recordType = OrientDB::RECORD_TYPE_DOCUMENT;
        if (count($this->attribs) == 3) {
            if (in_array($this->attribs[2], OrientDB::$recordTypes)) {
                $this->recordType = $this->attribs[2];
            } else {
                throw new OrientDBWrongParamsException('Incorrect record Type: ' . $this->attribs[2] . '. Available types is: ' . implode(', ', OrientDB::$recordTypes));
            }
        }
        $this->addByte($this->recordType);
        // Synchronous mode
        $this->addByte(chr($this->mode));
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return int
     */
    protected function parseResponse()
    {
        $this->debugCommand('record_pos');
        $position = $this->readLong();
        $this->debugCommand('record_version');
        $version = $this->readInt();

        if ($this->recordContent instanceof OrientDBRecord) {
            $this->recordContent->recordPos = $position;
            $this->recordContent->clusterID = $this->clusterID;
            $this->recordContent->version = $version;
        }

        return $position;
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * DBList() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandDBList extends OrientDBCommandAbstract
{

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::DB_LIST;
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return array
     */
    protected function parseResponse()
    {
        $this->debugCommand('database_list');
        $decoder = new OrientDBRecordDecoder($this->readString());
        return $decoder->data->databases;
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2013
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * DBOpen() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandDBOpen extends OrientDBCommandAbstract
{

    /**
     * SessionID of current connection
     * @var int
     */
    public $sessionID;

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::DB_OPEN;
    }

    public function prepare()
    {
        parent::prepare();
        if (count($this->attribs) != 3) {
            throw new OrientDBWrongParamsException('This command requires DB name, login and password');
        }
        // Add Driver name
        $this->addString(OrientDB::DRIVER_NAME);
        // Add Driver version
        $this->addString(OrientDB::DRIVER_VERSION);
        // Add protocol version
        $this->addShort($this->parent->getProtocolVersionClient());
        // Add client ID
        $this->addString('');
        // Add DB name
        $this->addString($this->attribs[0]);
        // Add DB type. Since rc9. Right now only document is supported
        $this->addString('document');
        // Add login
        $this->addString($this->attribs[1]);
        // Add password
        $this->addString($this->attribs[2]);
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return array
     */
    protected function parseResponse()
    {
        $this->debugCommand('sessionID');
        $this->sessionID = $this->readInt();

        $this->debugCommand('clusters');
        $numClusters = $this->readShort();

        $clusters = array();
        for ($i = 0; $i < $numClusters; $i++) {
            $cluster = new stdClass();
            $this->debugCommand('cluster_name');
            $cluster->name = $this->readString();
            $this->debugCommand('clusterID');
            $cluster->id = $this->readShort();
            $this->debugCommand('cluster_type');
            $cluster->type = $this->readString();
            $this->debugCommand('cluster_datasegment_id');
            $cluster->datasegmentid = $this->readShort();
            $clusters[] = $cluster;
        }
        $this->debugCommand('config_bytes');
        $config = $this->readBytes();
        $this->debugCommand('orientdb-release-string');
        $this->readString();

        return array(
            'clusters' => $clusters,
            'config' => $config);
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2012
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * command() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandCommand extends OrientDBCommandAbstract
{

    /**
     * Query text
     * @var string
     */
    protected $query;

    /**
     * Command mode
     * @var string
     */
    protected $mode;

    /**
     * Fetchplan
     * @var string
     */
    protected $fetchPlan;

    const MODE_SYNC = 's';

    const MODE_ASYNC = 'a';

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::COMMAND;
    }

    public function prepare()
    {
        parent::prepare();
        if (!empty($this->mode)) {
            // Populate $this->attribs with mode from child classes
            array_unshift($this->attribs, $this->mode);
        }
        if (count($this->attribs) > 3 || count($this->attribs) < 2) {
            throw new OrientDBWrongParamsException('This command requires query and, optionally, fetchplan');
        }
        if (empty($this->mode)) {
            if ($this->attribs[0] == OrientDB::COMMAND_SELECT_SYNC || $this->attribs[0] == OrientDB::COMMAND_SELECT_ASYNC || $this->attribs[0] == OrientDB::COMMAND_QUERY) {
                $this->mode = $this->attribs[0];
            } else {
                throw new OrientDBWrongParamsException('Wrong command mode');
            }
        }
        if (($this->mode == OrientDB::COMMAND_QUERY || $this->mode == OrientDB::COMMAND_SELECT_SYNC) && count($this->attribs) == 3) {
            throw new OrientDBWrongParamsException('Fetch is useless with COMMAND_QUERY');
        }
        $this->query = trim($this->attribs[1]);
        $this->fetchPlan = '*:0';
        if (count($this->attribs) == 3) {
            $this->fetchPlan = $this->attribs[2];
        }

        // Add mode
        if ($this->mode == OrientDB::COMMAND_QUERY || $this->mode == OrientDB::COMMAND_SELECT_SYNC || $this->mode == OrientDB::COMMAND_SELECT_GREMLIN) {
            $this->addByte(self::MODE_SYNC);
        } else {
            $this->addByte(self::MODE_ASYNC);
        }
        if ($this->mode == OrientDB::COMMAND_SELECT_ASYNC) {
            $objName = 'com.orientechnologies.orient.core.sql.query.OSQLAsynchQuery';
        } elseif ($this->mode == OrientDB::COMMAND_SELECT_SYNC) {
            $objName = 'com.orientechnologies.orient.core.sql.query.OSQLSynchQuery';
        } elseif ($this->mode == OrientDB::COMMAND_SELECT_GREMLIN) {
            $objName = 'com.orientechnologies.orient.graph.gremlin.OCommandGremlin';
        } else {
            $objName = 'com.orientechnologies.orient.core.sql.OCommandSQL';
        }
        // Java query object serialization
        $buff = '';
        // Java query object name serialization
        $buff .= pack('N', strlen($objName));
        $buff .= $objName;
        // Query text serialization in TEXT mode
        $buff .= pack('N', strlen($this->query));
        $buff .= $this->query;
        if ($this->mode == OrientDB::COMMAND_SELECT_ASYNC || $this->mode == OrientDB::COMMAND_SELECT_SYNC || $this->mode == OrientDB::COMMAND_SELECT_GREMLIN) {
            // Limit set to -1 to ignore and use TEXT MODE
            $buff .= pack('N', -1);
            // Add a fetchplan
            $buff .= pack('N', strlen($this->fetchPlan));
            $buff .= $this->fetchPlan;
        }
        // Params serialization, we have 0 params
        $buff .= pack('N', 0);
        // Now query object serialization complete, add it to command bytes
        $this->addString($buff);
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return bool|OrientDBTypeLink|OrientDBRecord|string|array
     */
    protected function parseResponse()
    {
        $this->debugCommand('status');
        $status = $this->readByte();
        if ($this->mode == OrientDB::COMMAND_SELECT_ASYNC) {
            if ($status != chr(0)) {
                $records = array();
                while ($status == chr(1)) {

                    $record = $this->readRecord();
                    $records[] = $record;

                    $this->debugCommand('record_status_next');
                    $status = $this->readByte();
                }
                // Cache records
                $cachedRecords = array();
                while ($status == chr(2)) {
                    $this->debugCommand('record_content');
                    $record = $this->readRecord();
                    $cachedRecords[$record->recordID] = $record;
                    $this->debugCommand('record_status_cache');
                    $status = $this->readByte();
                }
                // Invalidate cache
                $this->parent->cachedRecords = $cachedRecords;
                return $records;
            }
            return false;
        } else {
            if ($status == 'l') {
                // List of records
                $this->debugCommand('records_count');
                $recordsCount = $this->readInt();
                if ($recordsCount == 0) {
                    return false;
                }
                $records = array();
                for ($i = 0; $i < $recordsCount; $i++) {
                    $records[] = $this->readRecord();
                }
                return $records;
            } else if ($status == 'n') {
                // Null
                return null;
            } else if ($status == 'r') {
                // Single record
                return $this->readRecord();
            } else if ($status == 'a') {
                // Something other
                return $this->readString();
            }
        }
        return null;
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * dataclusterDatarange() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandDataclusterDatarange extends OrientDBCommandAbstract
{

    /**
     * ClusterID used in command
     * @var int
     */
    protected $clusterID;

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::DATACLUSTER_DATARANGE;
    }

    public function prepare()
    {
        parent::prepare();
        if (count($this->attribs) != 1) {
            throw new OrientDBWrongParamsException('This command requires cluster ID');
        }
        $this->clusterID = $this->attribs[0];
        if (!is_int($this->clusterID)) {
            throw new OrientDBWrongParamsException('Integer expected');
        }
        // Add clusterID
        $this->addShort($this->clusterID);
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return array
     */
    protected function parseResponse()
    {
        $this->debugCommand('start_pos');
        $startPos = $this->readLong();
        $this->debugCommand('end_pos');
        $endPos = $this->readLong();
        return array(
            'start' => $startPos,
            'end' => $endPos);
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011-2012
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * Class to decode OrientDB records
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Datatypes
 */
class OrientDBRecordDecoder
{

    /**
     * ClassName as parsed from document
     * @var string
     */
    public $className;

    /**
     * Document source as delivered from OrientDB
     * @var string
     */
    public $content;

    /**
     * A placeholder for document data
     * @var StdClass
     */
    public $data;

    /**
     * Parser state
     * @example self::STATE_GUESS
     * @var int
     */
    protected $state;

    /**
     * Parser buffer
     * @var string
     */
    protected $buffer;

    /**
     * Used to interrupt in case of embedded document
     * @var bool
     */
    protected $continue = true;

    /**
     * Current position in content
     * @var int
     */
    protected $i = 0;

    /**
     * Stack for token values
     * @var array
     */
    protected $stackTV = array();

    /**
     *
     * Stack fo token types
     * @var array
     */
    protected $stackTT = array();

    /**
     * List of possible states
     */

    /**
     * what we're going to collect?
     */
    const STATE_GUESS = 0;

    /**
     * collecting field name
     */
    const STATE_NAME = 1;

    /**
     * collecting field value
     */
    const STATE_VALUE = 2;

    /**
     * collecting double-quoted string
     */
    const STATE_STRING = 3;

    /**
     * collecting of comma between fields
     */
    const STATE_COMMA = 4;

    /**
     * collecting of link to other record
     */
    const STATE_LINK = 5;

    /**
     * collecting of number
     */
    const STATE_NUMBER = 6;

    /**
     * collecting of map key
     */
    const STATE_KEY = 7;

    /**
     * collecting boolean
     */
    const STATE_BOOLEAN = 8;

    /**
     * character classes
     */

    /**
     * char of word class [a-z-]
     */
    const CCLASS_WORD = 1;

    /**
     * char of number class [0-9]
     */
    const CCLASS_NUMBER = 2;

    /**
     * any other chars
     */
    const CCLASS_OTHER = 0;

    /**
     * Character codes
     */

    /**
     * @
     */
    const CCODE_AT = 0x40;

    /**
     * :
     */
    const CCODE_COLON = 0x3A;

    /**
     * "
     */
    const CCODE_DOUBLE_QUOTE = 0x22;

    /**
     * \
     */
    const CCODE_ESCAPE = 0x5C;

    /**
     * ,
     */
    const CCODE_COMMA = 0x2C;

    /**
     * (
     */
    const CCODE_OPEN_PARENTHESES = 0x28;

    /**
     * )
     */
    const CCODE_CLOSE_PARENTHESES = 0x29;

    /**
     * [ Set start
     */
    const CCODE_OPEN_ARROW = 0x3C;

    /**
     * ] Set end
     */
    const CCODE_CLOSE_ARROW = 0x3E;

    /**
     * [ List start
     */
    const CCODE_OPEN_SQUARE = 0x5B;

    /**
     * ] List end
     */
    const CCODE_CLOSE_SQUARE = 0x5D;

    /**
     * { Map start
     */
    const CCODE_OPEN_CURLY = 0x7B;

    /**
     * } Map end
     */
    const CCODE_CLOSE_CURLY = 0x7D;

    /**
     * *
     */
    const CCODE_ASTERISK = 0x2D;

    /**
     * #
     */
    const CCODE_HASH = 0x23;

    /**
     * .
     */
    const CCODE_PERIOD = 0x2E;

    /**
     * b
     */
    const CCODE_NUM_BYTE = 0x62;

    /**
     * s
     */
    const CCODE_NUM_SHORT = 0x73;

    /**
     * l
     */
    const CCODE_NUM_LONG = 0x6C;

    /**
     * f
     */
    const CCODE_NUM_FLOAT = 0x66;

    /**
     * c
     */
    const CCODE_NUM_DECIMAL = 0x63;

    /**
     * d
     */
    const CCODE_NUM_DOUBLE = 0x64;

    /**
     * f
     */
    const CCODE_BOOL_FALSE = 0x66;

    /**
     * t
     */
    const CCODE_BOOL_TRUE = 0x74;

    /**
     * -
     */
    const CCODE_MINUS = 0x2D;

    /**
     * e
     */
    const CCODE_EXP_LOWER = 0x65;

    /**
     * E
     */
    const CCODE_EXP_UPPER = 0x45;

    /**
     * t
     */
    const CCODE_DATE = 0x74;

    /**
     * token types
     */

    /**
     * Name of field
     */
    const TTYPE_NAME = 1;

    /**
     * ClassName
     */
    const TTYPE_CLASS = 2;

    /**
     * Null value
     */
    const TTYPE_NULL = 3;

    /**
     * String value
     */
    const TTYPE_STRING = 4;

    /**
     * Start of collection
     */
    const TTYPE_SET_START = 5;

    /**
     * End of collection
     */
    const TTYPE_SET_END = 6;

    /**
     * Link to recordID
     */
    const TTYPE_LINK = 7;

    /**
     * Numeric value
     */
    const TTYPE_NUMBER = 8;

    /**
     * Start of map
     */
    const TTYPE_MAP_START = 9;

    /**
     * End of map
     */
    const TTYPE_MAP_END = 10;

    /**
     * Boolean value
     */
    const TTYPE_BOOLEAN = 11;

    /**
     * Name of map key
     */
    const TTYPE_KEY = 12;

    /**
     * Embedded document
     */
    const TTYPE_EMBEDDED = 13;

    /**
     * Start of list
     */
    const TTYPE_LIST_START = 14;

    /**
     * End of list
     */
    const TTYPE_LIST_END = 15;

    public function __construct($content)
    {
        $this->content = $content;
        $this->decode();
    }

    /**
     * Parses $this->content and populates $this->data and $this->className
     * @throws OrientDBDeSerializeException
     * @return void
     */
    protected function decode()
    {
        // Parse record content
        // There is no need to use OrientDBRecordData here, as data will be copied for root record, and no parsing on demand for embedded records is made
        $this->data = new StdClass();
        // initial state
        $this->state = self::STATE_GUESS;
        // is parsing set
        $isSet = false;
        // is parsing list
        $isList = false;
        // is parsing a map
        $isMap = false;
        // is escape symbol
        $escape = false;

        $contentLength = strlen($this->content);

        while ($this->i <= $contentLength && $this->continue) {
            $char = substr($this->content, $this->i, 1);
            $cCode = ord($char);
            if (($cCode >= 0x41 && $cCode <= 0x5A) || ($cCode >= 0x61 && $cCode <= 0x7A) || $cCode === 0x5F) {
                $cClass = self::CCLASS_WORD;
            } elseif ($cCode >= 0x30 && $cCode <= 0x39) {
                $cClass = self::CCLASS_NUMBER;
            } else {
                $cClass = self::CCLASS_OTHER;
            }

            switch ($this->state) {
                case self::STATE_GUESS:
                    $this->state = self::STATE_NAME;
                    $this->buffer = $char;
                    $this->i++;
                    break;

                case self::STATE_NAME:
                    if ($cCode === self::CCODE_COLON) {
                        // Colon found - switch state to value collecting
                        $this->state = self::STATE_VALUE;
                        // fill token with data
                        $this->stackPush(self::TTYPE_NAME);
                    } elseif ($cCode === self::CCODE_AT) {
                        // @ found - this was class name
                        // start to collect name - no state change
                        // fill token with data
                        $this->stackPush(self::TTYPE_CLASS);
                    } else {
                        // Trying to fast-forward name collecting
                        if ($this->i < strlen($this->content)) {
                            // This can be field name or class name
                            $pos_colon = strpos($this->content, ':', $this->i);
                            $pos_at = strpos($this->content, '@', $this->i);
                            // Check, which one is closest
                            if ($pos_at !== false) {
                                $pos = min($pos_at, $pos_colon);
                            } else {
                                $pos = $pos_colon;
                            }
                        } else {
                            $pos = false;
                        }

                        if ($pos !== false && $pos > $this->i) {
                            // Position is found and we had enough length to perform fast-forward
                            $this->buffer .= substr($this->content, $this->i, ($pos - $this->i));
                            $this->i = $pos;
                            break;
                        }
                        // Still collecting name
                        $this->buffer .= $char;
                    }
                    $this->i++;
                    break;

                case self::STATE_KEY:
                    /**
                     * @TODO If map keys can contain escaping characters
                     */
                    if ($cCode === self::CCODE_COLON) {
                        // Colon found - switch state to value collecting
                        $this->state = self::STATE_VALUE;
                        // fill token with data
                        $this->stackPush(self::TTYPE_KEY);
                    } else {
                        // Fast-forwarding to " symbol
                        if ($this->i < strlen($this->content)) {
                            $pos = strpos($this->content, '"', $this->i);
                        } else {
                            $pos = false;
                        }
                        if ($pos !== false && $pos > $this->i) {
                            // Before " symbol
                            $this->buffer = substr($this->content, $this->i, ($pos - $this->i));
                            $this->i = $pos;
                        }
                    }
                    $this->i++;
                    break;

                case self::STATE_VALUE:
                    if ($cCode === self::CCODE_COMMA) {
                        // No value - switch state to comma
                        $this->state = self::STATE_COMMA;
                        // token type is null
                        $this->stackPush(self::TTYPE_NULL);
                    } elseif ($cCode === self::CCODE_DOUBLE_QUOTE) {
                        // switch state to string collecting
                        $this->state = self::STATE_STRING;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_HASH) {
                        // found hash - switch state to link
                        $this->state = self::STATE_LINK;
                        // add hash to value
                        $this->buffer = $char;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_OPEN_ARROW) {
                        // < found, state is still value
                        $this->state = self::STATE_VALUE;
                        // token type is set start
                        $this->stackPush(self::TTYPE_SET_START);
                        // started set
                        $isSet = true;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_CLOSE_ARROW) {
                        // > found,
                        $this->state = self::STATE_COMMA;
                        // token type is set end
                        $this->stackPush(self::TTYPE_SET_END);
                        // stopped set
                        $isSet = false;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_OPEN_SQUARE) {
                        // [ found, state is still value
                        $this->state = self::STATE_VALUE;
                        // token type is list start
                        $this->stackPush(self::TTYPE_LIST_START);
                        // started list
                        $isList = true;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_CLOSE_SQUARE) {
                        // ] found,
                        $this->state = self::STATE_COMMA;
                        // token type is collection end
                        $this->stackPush(self::TTYPE_LIST_END);
                        // stopped list
                        $isList = false;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_OPEN_CURLY) {
                        // found { switch state to name
                        $this->state = self::STATE_KEY;
                        // token type is map start
                        $this->stackPush(self::TTYPE_MAP_START);
                        // started map
                        $isMap = true;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_CLOSE_CURLY) {
                        // } found
                        // check if null value in the end of the map
                        if ($this->stackGetLastType() === self::TTYPE_KEY) {
                            // token type is map end
                            $this->stackPush(self::TTYPE_NULL);
                            break;
                        }
                        $this->state = self::STATE_COMMA;
                        // token type is map end
                        $this->stackPush(self::TTYPE_MAP_END);
                        // stopped map
                        $isMap = false;
                        $this->i++;
                    } elseif ($cCode === self::CCODE_OPEN_PARENTHESES) {
                        // ( found, state is COMMA
                        $this->state = self::STATE_COMMA;
                        // increment position so we can transfer clean document
                        $this->i++;
                        // create new parser
                        $parser = new OrientDBRecordDecoder(substr($this->content, $this->i));
                        // create new embedded document and populate its values
                        $tokenValue = new OrientDBRecord();
                        $tokenValue->data = $parser->data;
                        $tokenValue->className = $parser->className;
                        $tokenValue->setParsed();
                        // token type is embedded
                        $this->stackPush(self::TTYPE_EMBEDDED, $tokenValue);
                        // fast forward to embedded position
                        $this->i += $parser->i;
                        // increment counter so we can continue on clean document
                        $this->i++;
                        break;
                    } elseif ($cCode === self::CCODE_CLOSE_PARENTHESES) {
                        // end of current document reached
                        $this->continue = false;
                        break;
                    } elseif ($cCode === self::CCODE_BOOL_FALSE || $cCode === self::CCODE_BOOL_TRUE) {
                        // boolean found - switch state to boolean
                        $this->state = self::STATE_BOOLEAN;
                        $this->buffer = $char;
                        $this->i++;
                    } else {
                        if ($cClass === self::CCLASS_NUMBER || $cCode === self::CCODE_MINUS) {
                            // number found - switch to number collecting
                            $this->state = self::STATE_NUMBER;
                            $this->buffer = $char;
                            $this->i++;
                        } elseif ($char === false) {
                            $this->i++;
                        }
                    }
                    break;

                case self::STATE_COMMA:
                    if ($cCode === self::CCODE_COMMA) {
                        // Found a comma -  switch to
                        if ($isSet) {
                            $this->state = self::STATE_VALUE;
                        } elseif ($isList) {
                            $this->state = self::STATE_VALUE;
                        } elseif ($isMap) {
                            $this->state = self::STATE_KEY;
                        } else {
                            $this->state = self::STATE_GUESS;
                        }
                        $this->i++;
                    } else {
                        $this->state = self::STATE_VALUE;
                    }
                    break;

                case self::STATE_STRING:
                    // Check, if we can fast-forward to next " or \ symbol
                    if ($this->i < strlen($this->content)) {
                        // Separate search for symbols
                        $pos_quote = strpos($this->content, '"', $this->i);
                        $pos_escape = strpos($this->content, '\\', $this->i);
                        // Get first position
                        if ($pos_escape !== false) {
                            $pos = min($pos_quote, $pos_escape);
                        } else {
                            $pos = $pos_quote;
                        }
                    } else {
                        $pos = false;
                    }
                    if ($pos !== false) {
                        // If position is found
                        if ($pos > $this->i + 1) {
                            // And position is before any possible escape symbol
                            // Add to buffer
                            $this->buffer .= substr($this->content, $this->i, ($pos - $this->i - 1));
                            // Fast-forwarding
                            $this->i = $pos - 1;
                            break;
                        }
                    }

                    if ($cCode === self::CCODE_ESCAPE) {
                        // escaping 1 symbol
                        if ($escape === true) {
                            $this->buffer .= $char;
                            $escape = false;
                        } else {
                            $escape = true;
                        }
                    } elseif ($cCode === self::CCODE_DOUBLE_QUOTE) {
                        if ($escape === true) {
                            $this->buffer .= $char;
                            $escape = false;
                        } else {
                            // found end of string value - switch state to comma
                            $this->state = self::STATE_COMMA;
                            // token type is string
                            $this->stackPush(self::TTYPE_STRING);
                        }
                    } else {
                        // found next byte in string
                        $this->buffer .= $char;
                    }
                    $this->i++;
                    break;

                case self::STATE_LINK:
                    // Fast-forward
                    $result = preg_match('/\d+:\d+/', $this->content, $matches, PREG_OFFSET_CAPTURE, $this->i);
                    // And matches from current position
                    if ($result && $matches[0][1] === $this->i) {
                        $this->buffer = $matches[0][0];
                        $this->i += strlen($this->buffer);
                    } else {
                        // switch state to
                        if ($cCode === self::CCODE_COMMA) {
                            $this->state = self::STATE_COMMA;
                        } else {
                            $this->state = self::STATE_VALUE;
                        }
                        // token type is link
                        $this->stackPush(self::TTYPE_LINK, new OrientDBTypeLink($this->buffer));
                    }
                    break;

                case self::STATE_NUMBER:
                    // Fast-forward
                    $result = preg_match('/[\d\.e-]+/i', $this->content, $matches, PREG_OFFSET_CAPTURE, $this->i);
                    // And matches from current position
                    if ($result && $matches[0][1] === $this->i) {
                        $this->buffer .= $matches[0][0];
                        $this->i += strlen($matches[0][0]);
                    } else {
                        // switch state to
                        if ($cCode === self::CCODE_COMMA) {
                            $this->state = self::STATE_COMMA;
                        } elseif ($cClass === self::CCLASS_WORD) {
                            $this->state = self::STATE_COMMA;
                        } else {
                            $this->state = self::STATE_VALUE;
                        }
                        // fill token
                        if ($cCode === self::CCODE_NUM_BYTE || $cCode === self::CCODE_NUM_SHORT) {
                            $tokenValue = (int) $this->buffer;
                            $this->i++;
                        } elseif ($cCode === self::CCODE_NUM_LONG || $cCode === self::CCODE_NUM_FLOAT || $cCode === self::CCODE_NUM_DOUBLE || $cCode === self::CCODE_NUM_DECIMAL) {
                            $tokenValue = (float) $this->buffer;
                            $this->i++;
                        } elseif ($cCode === self::CCODE_DATE) {
                            // This is datetime
                            $tokenValue = new OrientDBTypeDate($this->buffer);
                            $this->i++;
                        } else {
                            // this is int
                            $tokenValue = (int) $this->buffer;
                        }
                        // token type is number
                        $this->stackPush(self::TTYPE_NUMBER, $tokenValue);
                    }
                    break;

                case self::STATE_BOOLEAN:
                    // Fast-forward
                    // @TODO It's possible to gain more speed by checking not entire literal, but only first (really, second) character
                    if (strpos($this->content, 'rue', $this->i) === $this->i) {
                        $tokenValue = true;
                        $this->i += 3;
                    } elseif (strpos($this->content, 'alse', $this->i) === $this->i) {
                        $tokenValue = false;
                        $this->i += 4;
                    } else {
                        throw new OrientDBDeSerializeException('Can\'t de-serialize boolean value on key "' . $this->stackGetLastKey() . '"');
                    }
                    // found end of boolean value - switch state to comma
                    $this->state = self::STATE_COMMA;
                    // token value is boolean
                    $this->stackPush(self::TTYPE_BOOLEAN, $tokenValue);
                    break;

                default:
                    return;
                    break;
            }

            switch ($this->stackGetLastType()) {
                case false:
                case self::TTYPE_NAME:
                case self::TTYPE_KEY:
                case self::TTYPE_SET_START:
                case self::TTYPE_LIST_START:
                case self::TTYPE_MAP_START:
                    // some speed up
                    break;

                case self::TTYPE_CLASS:
                    list (, $value) = $this->stackPop();
                    $this->className = $value;
                    break;

                case self::TTYPE_STRING:
                case self::TTYPE_LINK:
                case self::TTYPE_NUMBER:
                case self::TTYPE_BOOLEAN:
                case self::TTYPE_EMBEDDED:
                    if (!$isSet && !$isMap && !$isList) {
                        list (, $value) = $this->stackPop();
                        list (, $name) = $this->stackPop();
                        $this->data->$name = $value;
                    }
                    break;

                case self::TTYPE_NULL:
                    if (!$isSet && !$isMap && !$isList) {
                        $this->stackPop();
                        list (, $name) = $this->stackPop();
                        $this->data->$name = null;
                    }
                    break;

                case self::TTYPE_SET_END:
                    $values = array();
                    do {
                        list ($searchToken, $value) = $this->stackPop();

                        if ($searchToken !== self::TTYPE_SET_START && $searchToken !== self::TTYPE_SET_END) {
                            $values[] = $value;
                        }
                    } while ($searchToken !== self::TTYPE_SET_START);
                    list (, $name) = $this->stackPop();
                    $values = array_reverse($values);
                    $this->data->$name = $values;
                    break;

                case self::TTYPE_LIST_END:
                    $values = array();
                    do {
                        list ($searchToken, $value) = $this->stackPop();

                        if ($searchToken !== self::TTYPE_LIST_START && $searchToken !== self::TTYPE_LIST_END) {
                            $values[] = $value;
                        }
                    } while ($searchToken !== self::TTYPE_LIST_START);
                    list (, $name) = $this->stackPop();
                    $values = array_reverse($values);
                    $this->data->$name = $values;
                    break;

                case self::TTYPE_MAP_END:
                    $values = array();
                    do {
                        list ($searchToken, $value) = $this->stackPop();
                        // check for null value
                        if ($searchToken === self::TTYPE_NULL) {
                            $value = null;
                        }
                        if ($searchToken !== self::TTYPE_MAP_START && $searchToken !== self::TTYPE_MAP_END) {
                            list (, $key) = $this->stackPop();
                            $values[$key] = $value;
                        }
                    } while ($searchToken !== self::TTYPE_MAP_START);
                    list (, $name) = $this->stackPop();
                    $values = array_reverse($values, true);
                    $this->data->$name = $values;
                    break;

                default:
                    break;
            }
        }
    }

    /**
     * Pushes found value to internal stack and flushes $this->buffer. If no
     * $tokenValue is given, uses $this->buffer
     * @param int $tokenType
     * @param mixed $tokenValue
     */
    protected function stackPush($tokenType, $tokenValue = null)
    {
        array_push($this->stackTT, $tokenType);
        if ($tokenValue === null) {
            $tokenValue = $this->buffer;
        }
        array_push($this->stackTV, $tokenValue);
        $this->buffer = '';
    }

    /**
     * Pop value from internal stack
     * @return array
     */
    protected function stackPop()
    {
        return array(
            array_pop($this->stackTT),
            array_pop($this->stackTV));
    }

    /**
     * Return last token type
     * @return int
     * @example TTYPE_NAME
     */
    protected function stackGetLastType()
    {
        return end($this->stackTT);
    }

    /**
     * Return string of last found key
     * @return string
     */
    protected function stackGetLastKey()
    {
        $depth = false;
        for ($i = count($this->stackTT) - 1; $i >= 0; $i--) {
            if ($this->stackTT[$i] === self::TTYPE_NAME) {
                $depth = $i;
                break;
            }
        }
        if ($depth !== false) {
            return $this->stackTV[$depth];
        }
        return null;
    }
}


<?php

/**
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @copyright Copyright Anton Terekhov, NetMonsters LLC, 2011
 * @license https://github.com/AntonTerekhov/OrientDB-PHP/blob/master/LICENSE
 * @link https://github.com/AntonTerekhov/OrientDB-PHP
 * @package OrientDB-PHP
 */

/**
 * configList() command for OrientDB-PHP
 *
 * @author Anton Terekhov <anton@netmonsters.ru>
 * @package OrientDB-PHP
 * @subpackage Command
 */
class OrientDBCommandConfigList extends OrientDBCommandAbstract
{

    public function __construct($parent)
    {
        parent::__construct($parent);
        $this->opType = OrientDBCommandAbstract::CONFIG_LIST;
    }

    /**
     * (non-PHPdoc)
     * @see OrientDBCommandAbstract::parseResponse()
     * @return array
     */
    protected function parseResponse()
    {
        $this->debugCommand('options_count');
        $numOptions = $this->readShort();

        $options = array();
        for ($i = 0; $i < $numOptions; $i++) {
            $this->debugCommand('option_name');
            $optionName = $this->readString();
            $this->debugCommand('option_value');
            $options[$optionName] = $this->readString();
        }

        return $options;
    }
}