diff --git a/composer.json b/composer.json index c1c4d86..2787916 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "ostico/phporient", "description": "Good enough PHP binary client for OrientDB.", - "version": "v1.1.7", + "version": "v1.1.8", "keywords": [ "orientdb", "orient db", diff --git a/src/PhpOrient/Configuration/Constants.php b/src/PhpOrient/Configuration/Constants.php index 7b8fd66..9758c99 100644 --- a/src/PhpOrient/Configuration/Constants.php +++ b/src/PhpOrient/Configuration/Constants.php @@ -17,7 +17,7 @@ class Constants { const VERSION = "v1.1.7"; const ID = "1"; const NAME = "PhpOrient - PHP binary client for OrientDB"; - const SUPPORTED_PROTOCOL = 28; + const SUPPORTED_PROTOCOL = 29; public static $LOGGING = false; public static $LOG_FILE_PATH = false; diff --git a/src/PhpOrient/Protocols/Binary/Data/Bag.php b/src/PhpOrient/Protocols/Binary/Data/Bag.php index b1947f9..50ccbfc 100644 --- a/src/PhpOrient/Protocols/Binary/Data/Bag.php +++ b/src/PhpOrient/Protocols/Binary/Data/Bag.php @@ -11,7 +11,7 @@ * * @package OrientDB\Records */ -class Bag implements \Countable, \ArrayAccess, \Iterator { +class Bag implements \Countable, \ArrayAccess, \Iterator, \JsonSerializable { const EMBEDDED = 0; const TREE = 1; @@ -19,12 +19,12 @@ class Bag implements \Countable, \ArrayAccess, \Iterator { /** * @var string The base64 encoded representation of the bag. */ - protected $serialized; + protected $base64Content; /** * @var string The base64 decoded stream of bytes. */ - protected $deserialized; + protected $binaryContent; /** * @var int The bag type, either embedded or tree. @@ -84,10 +84,10 @@ class Bag implements \Countable, \ArrayAccess, \Iterator { /** * # RIDBag Constructor * - * @param string $serialized the base64 encoded bag + * @param string $base64Content the base64 encoded bag */ - public function __construct( $serialized ) { - $this->serialized = $serialized; + public function __construct( $base64Content ) { + $this->base64Content = $base64Content; } /** @@ -114,12 +114,52 @@ public function getSize() { return $this->size; } + /** + * Get the list of contained RIDs + * @return array + */ + public function getRids(){ + if( empty( $this->items ) ){ + foreach ( $this as $idx => $rid ){ + //NOP + //Call this cycle to decode the bag + } + } + return $this->items; + } + + /** + * Get the original raw content for the Bag + * + * @return string + */ + public function getRawBagContent(){ + return "%" . $this->base64Content . ";"; + } + + /** + * (PHP 5 >= 5.4.0)
+ * Specify data which should be serialized to JSON + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + */ + public function jsonSerialize(){ + return $this->getRids(); + } + /** * Parse the bag header. */ protected function parse() { - $this->deserialized = base64_decode( $this->serialized ); - $mode = ord( $this->deserialized[ 0 ] ); + + if( !empty( $this->binaryContent ) ){ + //Already parsed + return; + } + + $this->binaryContent = base64_decode( $this->base64Content ); + $mode = ord( $this->binaryContent[ 0 ] ); if ( ( $mode & 1 ) === 1 ) { $this->type = self::EMBEDDED; @@ -128,7 +168,7 @@ protected function parse() { } if ( ( $mode & 2 ) === 2 ) { - $this->uuid = substr( $this->deserialized, 1, 16 ); + $this->uuid = substr( $this->binaryContent, 1, 16 ); $this->ReaderOffset = 17; } else { $this->ReaderOffset = 1; @@ -145,7 +185,7 @@ protected function parse() { * Parse the header for an embedded bag. */ protected function parseEmbedded() { - $this->size = Reader::unpackInt( substr( $this->deserialized, $this->ReaderOffset, 4 ) ); + $this->size = Reader::unpackInt( substr( $this->binaryContent, $this->ReaderOffset, 4 ) ); $this->ReaderOffset += 4; $this->baseOffset = $this->ReaderOffset; } @@ -154,19 +194,19 @@ protected function parseEmbedded() { * Parse the header for a tree bag. */ protected function parseTree() { - $this->fileId = Reader::unpackLong( substr( $this->deserialized, $this->ReaderOffset, 8 ) ); + $this->fileId = Reader::unpackLong( substr( $this->binaryContent, $this->ReaderOffset, 8 ) ); $this->ReaderOffset += 8; - $this->pageIndex = Reader::unpackLong( substr( $this->deserialized, $this->ReaderOffset, 8 ) ); + $this->pageIndex = Reader::unpackLong( substr( $this->binaryContent, $this->ReaderOffset, 8 ) ); $this->ReaderOffset += 8; - $this->pageOffset = Reader::unpackInt( substr( $this->deserialized, $this->ReaderOffset, 4 ) ); + $this->pageOffset = Reader::unpackInt( substr( $this->binaryContent, $this->ReaderOffset, 4 ) ); $this->ReaderOffset += 4; - $this->size = Reader::unpackInt( substr( $this->deserialized, $this->ReaderOffset, 4 ) ); + $this->size = Reader::unpackInt( substr( $this->binaryContent, $this->ReaderOffset, 4 ) ); $this->ReaderOffset += 4; - $this->changeSize = Reader::unpackInt( substr( $this->deserialized, $this->ReaderOffset, 4 ) ); + $this->changeSize = Reader::unpackInt( substr( $this->binaryContent, $this->ReaderOffset, 4 ) ); $this->ReaderOffset += 4; } @@ -259,14 +299,14 @@ public function offsetGet( $offset ) { if ( $this->type === self::EMBEDDED ) { $start = $this->baseOffset + ( $offset * 10 ); - $chunk = substr( $this->deserialized, $start, 2 ); + $chunk = substr( $this->binaryContent, $start, 2 ); if( $chunk === false ){ $this->items[ $offset ] = false; return $this->items[ $offset ]; } - $cluster = Reader::unpackShort( substr( $this->deserialized, $start, 2 ) ); - $position = Reader::unpackLong( substr( $this->deserialized, $start + 2, 8 ) ); + $cluster = Reader::unpackShort( substr( $this->binaryContent, $start, 2 ) ); + $position = Reader::unpackLong( substr( $this->binaryContent, $start + 2, 8 ) ); $this->items[ $offset ] = new ID( $cluster, $position ); } else { $this->items[ $offset ] = false; diff --git a/src/PhpOrient/Protocols/Binary/Operations/DbOpen.php b/src/PhpOrient/Protocols/Binary/Operations/DbOpen.php index 7ce21f0..055ecab 100644 --- a/src/PhpOrient/Protocols/Binary/Operations/DbOpen.php +++ b/src/PhpOrient/Protocols/Binary/Operations/DbOpen.php @@ -139,7 +139,6 @@ protected function _read() { } - //TODO: Try with a cluster instance # cluster config string ( -1 ) # cluster release $cluster_list = [ diff --git a/src/PhpOrient/Protocols/Binary/Serialization/CSV.php b/src/PhpOrient/Protocols/Binary/Serialization/CSV.php index 0434455..a93d99b 100644 --- a/src/PhpOrient/Protocols/Binary/Serialization/CSV.php +++ b/src/PhpOrient/Protocols/Binary/Serialization/CSV.php @@ -2,6 +2,7 @@ namespace PhpOrient\Protocols\Binary\Serialization; +use PhpOrient\Configuration\Constants; use PhpOrient\Protocols\Binary\Abstracts\SerializableInterface; use PhpOrient\Protocols\Binary\Data\Bag; use PhpOrient\Protocols\Binary\Data\ID; @@ -514,6 +515,29 @@ public static function serialize( $value, $embedded = false ) { return $value->getTimestamp() . 't'; } elseif ( $value instanceof ID ) { return $value->__toString(); + } elseif ( $value instanceof Bag ){ + /* + * This line works the same, but transforms the edges list to a linkSet + * // return self::serializeArray( $value->getRids() ); + * + * From: + * + * ----+-----+------+------+--------+--------- + * # |@RID |@CLASS|script|out_ |in_ + * ----+-----+------+------+--------+--------- + * 0 |#9:0 |V |true |[size=1]|[size=1] + * ----+-----+------+------+--------+--------- + * + * To: + * + * ----+-----+------+------+--------+--------- + * # |@RID |@CLASS|script|out_ |in_ + * ----+-----+------+------+--------+--------- + * 0 |#9:0 |V |true |[1] |[1] + * ----+-----+------+------+--------+--------- + * + */ + return $value->getRawBagContent(); } else { return ''; } diff --git a/src/PhpOrient/Protocols/Binary/SocketTransport.php b/src/PhpOrient/Protocols/Binary/SocketTransport.php index 6f74fee..89216e6 100644 --- a/src/PhpOrient/Protocols/Binary/SocketTransport.php +++ b/src/PhpOrient/Protocols/Binary/SocketTransport.php @@ -279,9 +279,9 @@ public function hexDump( $message ){ if( Constants::$LOGGING ){ $_msg = self::_hexDump( $message ); $rows = explode( "\n", $_msg ); - self::$_logger->debug( "\n" ); + $this->_logger->debug( "\n" ); foreach( $rows as $row ){ - self::$_logger->debug( $row ); + $this->_logger->debug( $row ); } } } diff --git a/src/PhpOrient/Protocols/Common/AbstractTransport.php b/src/PhpOrient/Protocols/Common/AbstractTransport.php index c84cb6b..128d2ff 100644 --- a/src/PhpOrient/Protocols/Common/AbstractTransport.php +++ b/src/PhpOrient/Protocols/Common/AbstractTransport.php @@ -38,22 +38,33 @@ abstract class AbstractTransport implements TransportInterface { /** * @var LoggerInterface */ - protected static $_logger; + protected $_logger; /** * Class Constructor + * + * @throws PhpOrientException */ public function __construct() { + $this->setLogger(); + } + + /** + * Set the client Logger + * + * @throws PhpOrientException + */ + public function setLogger(){ if ( ClientConstants::$LOGGING ) { - if ( self::$_logger === null ) { + if ( $this->_logger === null ) { if( !class_exists( '\Monolog\Logger', true ) ){ throw new PhpOrientException( "No development environment installed from composer. Try 'composer update' or remove logging from client constants ( \\PhpOrient\\Configuration\\Constants::\$LOGGING )" ); } - self::$_logger = new \Monolog\Logger( get_class( $this ) ); + $this->_logger = new \Monolog\Logger( get_class( $this ) ); $file_path = "php://stdout"; if ( ClientConstants::$LOG_FILE_PATH ) { @@ -62,18 +73,36 @@ public function __construct() { $handler = new \Monolog\Handler\StreamHandler( $file_path, \Monolog\Logger::DEBUG ); $handler->setFormatter( new \Monolog\Formatter\LineFormatter( null, null, false, true ) ); - self::$_logger->pushHandler( $handler ); + $this->_logger->pushHandler( $handler ); } } else { - self::$_logger = new NullLogger(); + $this->_logger = new NullLogger(); } } + /** + * Get the Logger from transport + * + * @return LoggerInterface + * @throws PhpOrientException + */ + public function getLogger(){ + if( empty( $this->_logger ) ) { + $this->setLogger(); + } + return $this->_logger; + } + + /** + * Debug method + * + * @param $message + */ public function debug( $message ){ - self::$_logger->debug( $message ); + $this->_logger->debug( $message ); } /** diff --git a/src/PhpOrient/Protocols/Common/ClusterMap.php b/src/PhpOrient/Protocols/Common/ClusterMap.php index bae86af..dcc436b 100644 --- a/src/PhpOrient/Protocols/Common/ClusterMap.php +++ b/src/PhpOrient/Protocols/Common/ClusterMap.php @@ -59,6 +59,21 @@ class ClusterMap implements ConfigurableInterface, \ArrayAccess, \Countable, \It */ protected $release; + /** + * @var int + */ + protected $majorVersion; + + /** + * @var int + */ + protected $minorVersion; + + /** + * @var string + */ + protected $buildNumber; + /** * @return int */ @@ -66,6 +81,19 @@ public function getServers() { return $this->servers; } + protected function _parseRelease(){ + @list( + $this->majorVersion, + $this->minorVersion, + $this->buildNumber + ) = @explode( ".", $this->release ); + + if ( stripos( $this->minorVersion, "-" ) !== false ){ + @list( $this->minorVersion, $this->buildNumber ) = explode( "-", $this->minorVersion ); + } + @list( $this->buildNumber, ) = explode( " ", $this->buildNumber ); + } + /** * @return string */ @@ -73,7 +101,26 @@ public function getRelease() { return $this->release; } + /** + * @return int + */ + public function getMajorVersion() { + return (int)$this->majorVersion; + } + + /** + * @return int + */ + public function getMinorVersion() { + return (int)$this->minorVersion; + } + /** + * @return string + */ + public function getBuildNumber() { + return $this->buildNumber; + } /** * Expected ClusterMap @@ -99,6 +146,7 @@ public function getRelease() { public function configure( Array $options = array() ) { $this->config( $options ); + $this->_parseRelease(); if ( !empty( $this->dataClusters ) ) { $this->reverseMap = array(); $this->reverseIDMap = array(); diff --git a/tests/PhpOrient/RecordCommandsTest.php b/tests/PhpOrient/RecordCommandsTest.php index 375adda..f429113 100644 --- a/tests/PhpOrient/RecordCommandsTest.php +++ b/tests/PhpOrient/RecordCommandsTest.php @@ -8,7 +8,9 @@ namespace PhpOrient; use PhpOrient\Abstracts\TestCase; - +use PhpOrient\Configuration\Constants as ClientConstants; +use PhpOrient\Protocols\Common\AbstractTransport; +use PhpOrient\Protocols\Common\Constants; use PhpOrient\Protocols\Binary\Data\ID; use PhpOrient\Protocols\Binary\Data\Record; @@ -305,4 +307,71 @@ public function testHiLevelCreateDelete(){ $this->assertEmpty( $result ); } + public function testUpdateEdges(){ + +// ClientConstants::$LOGGING = true; +// ClientConstants::$LOG_FILE_PATH = "php://stdout"; + + $client = PhpOrient::fromConfig( + array( + 'username' => 'admin', + 'password' => 'admin', + 'hostname' => 'localhost', + 'port' => 2424 + ) + ); + + $res = $client->execute('connect'); + + try { + $client->dbDrop( "db_test_edges", Constants::STORAGE_TYPE_MEMORY ); + } catch ( \Exception $e ) { + echo $e->getMessage(); + $client->getTransport()->debug( $e->getMessage() ); + } + + $client->dbCreate( "db_test_edges", + Constants::STORAGE_TYPE_MEMORY, + Constants::DATABASE_TYPE_GRAPH + ); + + $orientInfo = $client->dbOpen( "db_test_edges", 'admin', 'admin' ); + + $cmd = 'begin;' . + 'let a = create vertex set script = true;' . + 'let b = select from v limit 1;' . + 'let e = create edge from $a to $b;' . + 'commit retry 100;'; + + $lastRecord = $client->sqlBatch( $cmd ); + $lastRecord = $client->sqlBatch( $cmd ); + $lastRecord = $client->sqlBatch( $cmd ); + $lastRecord = $client->sqlBatch( $cmd ); + $lastRecord = $client->sqlBatch( $cmd ); + + $rec = $client->recordLoad( new ID("#9:0") )[0]; + + /** + * @var $bag \PhpOrient\Protocols\Binary\Data\Bag + */ + $bag = $rec->getOData()['in_']; + $this->assertNotEmpty( $bag->getRawBagContent() ); + $client->recordUpdate($rec); + + /** + * @var $bag2 \PhpOrient\Protocols\Binary\Data\Bag + */ + $rec = $client->recordLoad( new ID("#9:0") )[0]; + $bag2 = $rec->getOData()['in_']; + $this->assertNotEmpty( $bag2->getRawBagContent() ); + + if( $orientInfo->getMajorVersion() >= 2 + && $orientInfo->getMinorVersion() >= 0 + && ( $orientInfo->getBuildNumber() >= 7 || !is_numeric( $orientInfo->getBuildNumber() ) ) + ) { + $this->assertEquals( $bag->getRawBagContent(), $bag2->getRawBagContent() ); + } + + } + } \ No newline at end of file