OrientDB-PHP
A plain PHP driver to OrientDB graph database using its binary protocol.
Description
Current status is: Beta. (Meaning specs can be changed slightly. However, driver is surely usable right now)
Code is licensed under New BSD License and provided "as is". For complete license information see file LICENSE
.
Current OrientDB version to work with is: 1.5.0-RELEASE
(2013-07-30) (revision 4d28095).
It can be downloaded from OrientDB's Wiki Downloads page.
Code compatible to previous binary releases of OrientDB can be found in repository's tags or in Downloads section.
Current protocol version implemented: 15
Requirements
This library requires:
- PHP 5.3.x
- spl extension (since PHP 5.3.0 this extension is always available)
- PCRE extension (as of PHP 5.3.0 this extension cannot be disabled and is therefore always present)
- bcmath extension (Since PHP 4.0.4, libbcmath is bundled with PHP. These functions are only available if PHP was configured with --enable-bcmath .). Used on 32bit systems for dealing with 64bit long.
If PHP 5.3.x is a concern, you can try to run this code in version 5.2.x, however, this is not supported.
Installing OrientDB-PHP
Main public repository of OrientDB-PHP is hosted at https://github.com/AntonTerekhov/OrientDB-PHP.
To install most recent version of library, just type
git clone git://github.com/AntonTerekhov/OrientDB-PHP.git
where you want its file to be located.
You can also install library via Composer: https://packagist.org/packages/orientdb-php/orientdb-php
You can also want to get latest stable version, so check out Downloads section. Stables are marked with tags including this library version and OrientDB version.
Using OrientDB-PHP
OrientDB-PHP uses autoload functionality, so you only need to include OrientDB.php
file.
require 'OrientDB/OrientDB.php';
For a complex usage example see file example.php
.
Testing OrientDB-PHP
OrientDB-PHP is covered with automatic tests by phpUnit. Tests are located in Tests/
directory.
You can always re-test the whole library by typing
phpunit Tests/
Don't forget to change root password BEFORE running tests.
Function list
Some functions requires to be already connected to OrientDB server (using connect()
) or to have database opened (using DBOpen()
). This can be referenced at protocol description. If sequence is wrong - exception OrientDBWrongCommandException
will be thrown and no interaction with server will be made.
Create a new instance of OrientDB class
$db = new OrientDB(string $host[, int $port[, int $connectTimeout]]);
Example:
$db = new OrientDB('localhost', 2424);
Connect to server
Connects to OrientDB server (not database) with user and password specified.
Returns true
on success or throws exception.
bool $db->connect(string $userName, string $password);
Example:
$connected = $db->connect('root', 'passwd');
Database functions
DBOpen
Open database for work with or throws exception on failure (non-existent DB, wrong login or password). Return array consist of cluster information and config.
array $db->DBOpen(string $dbName, string $userName, string $password);
Example:
$config = $db->DBOpen('demo', 'writer', 'writer');
DBClose
Closes currently opened database.
Silently closes currently opened database, if any. Socket to OrientDB server is closed, and no further commands are possible. Will throw an exception if no database are open on OrientDB instance.
void $db->DBClose();
DBCreate
Creates new database. Return true
on success or throw an exception.
bool $db->DBCreate(string $dbName, string $dbType);
Available types is:
OrientDB::DB_TYPE_MEMORY
for in memory databaseOrientDB::DB_TYPE_LOCAL
for physical database
For difference see official OrientDB docs.
Example:
$isCreated = $db->DBCreate('mydb', OrientDB::DB_TYPE_LOCAL);
DBDelete
Delete database with name provided. Always return true
.
bool $db->DBDelete(string $dbName);
Example:
$result = $db->DBDelete('testdb');
DBExists
Checks if database with name provided is exists. Return true
on success, false
is no database exists or throws an exception.
bool $db->DBExists(string $dbName);
Example:
$isExists = $db->DBExists('demo');
DBList
Returns list of databases on server as array, where key is database name and value is string with schema and full disk path to database files on server.
array $db->DBList();
Example:
$list = $db->DBExists('demo');
var_dump($list);
Can produce something like:
array(3) {
["demo"]=>
string(76) "local:/home/orientdb/databases/demo"
["temp"]=>
string(11) "memory:temp"
}
Record manipulation functions
recordCreate
Create record in specified cluster with content and type. Returns record position in cluster.
int $db->recordCreate( int $clusterID, string|OrientDBRecord $recordContent[, string $recordType]);
Available record types are:
OrientDB::RECORD_TYPE_BYTES
OrientDB::RECORD_TYPE_DOCUMENT
OrientDB::RECORD_TYPE_FLAT
Default type used is OrientDB::RECORD_TYPE_DOCUMENT
.
Example 1:
$recordPos = $db->recordCreate(1, 'name:"John"');
You can, however, use instance of class OrientDBRecord to create new entry in OrientDB server. If so, some of this instance properties (clusterID
, recordPos
, recordID
, version
) will be filled with correct values. See example below:
Example 2:
$record = new OrientDBRecord();
$record->data->name = 'John';
$recordPos = $db->recordCreate(1, $record);
echo $record->recordPos . PHP_EOL;
echo $record->clusterID . PHP_EOL;
echo $record->recordID . PHP_EOL;
echo $record->version . PHP_EOL;
Can produce something like:
1
5
1:5
0
Due to PHP's behavior, objects are always passed by reference instead of int, for example. This, if automatically updating of record fields is not an option, can get you in trouble. So, in that case you should see example below:
Example 3:
$record = new OrientDBRecord();
$record->data->name = 'John';
$recordPos = $db->recordCreate(1, (string) $record);
Please, note, that using OrientDBRecord instance in methods (for example, recordCreate
or recordCreate
) doesn't automatically fill up other method parameters, you still have to pass them manually.
recordDelete
Delete record with specified recordID and optionally, version.
Returns true
on success, false
otherwise or throws an exception.
bool $db->recordDelete(string $recordID[, int $recordVersion]);
Default version is -1
. This means no version check will be done.
Example:
$result = $db->recordDelete('1:1');
$result = $db->recordDelete('1:1', 1);
recordLoad
Load record by recordID and, optionally, fetchplan. Returns record or false
. In some cases (e.g. recordPos is out of file bounds) can throw an exception
OrientDBRecord $db->recordLoad(string $recordID[, string $fetchPlan]);
Default fetchplan is *:0
, which mean load only record specified.
Example:
$record = $db->recordLoad('1:1');
If fetchplan is explicit and there are some records returned by OrientDB, they located in $db->cachedRecords
as associative array with keys from recordIDs and values are record themselves.
This example
$record = $db->recordLoad('1:1', '*:-1');
var_dump($db->cachedRecords);
Will produce something like this:
array(2) {
["11:0"]=>
object(OrientDBRecord)#178 (8) {
...
During next call to any method which is able to populate $db->cachedRecords
(e.g. recordLoad()
or command()
) this array will be reset.
recordUpdate
Update record with specified recordID and, optionally, version. Returns new record version on success, -1 otherwise or throws an exception.
int $db->recordUpdate(string $recordID, string|OrientDBRecord $recordContent[, int $recordVersion[, string $recordType]]);
Default version is -1
. This means no version check will be done.
Available record types are:
OrientDB::RECORD_TYPE_BYTES
OrientDB::RECORD_TYPE_DOCUMENT
OrientDB::RECORD_TYPE_FLAT
Default type used is OrientDB::RECORD_TYPE_DOCUMENT
.
Examples 1:
$version = $db->recordUpdate('1:1', 'Name:"Bob"');
$version = $db->recordUpdate('1:1', 'Name:"Sam"', 1, OrientDB::RECORD_TYPE_DOCUMENT);
You can, however, use instance of class OrientDBRecord to update record in OrientDB server. If so, some of this instance properties (clusterID
, recordPos
, recordID
, version
) will be filled with correct values. See example below:
Example 2:
$record = new OrientDBRecord();
$record->data->name = 'John';
$recordPos = $db->recordUpdate('1:1', $record);
echo $record->recordPos . PHP_EOL;
echo $record->clusterID . PHP_EOL;
echo $record->recordID . PHP_EOL;
echo $record->version . PHP_EOL;
Can produce something like:
1
1
1:1
3
Due to PHP's behavior, objects are always passed by reference instead of int, for example. This, if automatically updating of record fields is not an option, can get you in trouble. So, in that case you should see example below:
Example 3:
$record = new OrientDBRecord();
$record->data->name = 'John';
$recordPos = $db->recordUpdate('1:1', (string) $record);
Please, note, that using OrientDBRecord instance in methods (for example, recordCreate
or recordCreate
) doesn't automatically fill up other method parameters, you still have to pass them manually.
Config commands
configList
Get list of configurable options. Returns associative array with keys from option names and values themselves.
array $db->configList();
Example:
$options = $db->configList();
configGet
Get value for config option. Returns value as string
. If option name not found returns empty string
.
string $db->configGet(string $optionName);
Example:
$value = $db->configGet('log.console.level');
configSet
Set value for config option. Returns true
on success or throws an exception.
bool $db->configSet(string $optionName, string $optionValue);
Example:
$result = $db->configSet('log.console.level', 'info');
Datacluster commands
dataclusterAdd
Add new datacluster with specified name and type. Returns new cluster ID or throws an exception.
int $db->dataclusterAdd(string $clusterName, string $clusterType);
Cluster types available are:
OrientDB::DATACLUSTER_TYPE_LOGICAL
OrientDB::DATACLUSTER_TYPE_PHYSICAL
OrientDB::DATACLUSTER_TYPE_MEMORY
Example:
$clusterID = $db->dataclusterAdd('testcluster', OrientDB::DATACLUSTER_TYPE_PHYSICAL);
dataclusterRemove
Removes datacluster by its ID. Returns true
on success or throws an exception.
bool $db->dataclusterRemove(int $clusterID);
Example:
$result = $db->dataclusterRemove(10);
dataclusterCount
Counts elements in clusters specified by cluster IDs. Returns count or throws an exception.
int $db->dataclusterCount(array $clusterIDs);
Example:
$count = $db->dataclusterCount(array(1, 2));
dataclusterDatarange
Returns datarange for specified cluster ID. Returns array of start
and end
positions or throws an exception.
array $db->dataclusterDatarange(int $clusterID);
Example:
$data = $db->dataclusterDatarange(int $clusterID);
array(2) {
["start"]=>
int(0)
["end"]=>
int(126)
}
Datasegment commands
Not implemented yet.
commit
Commits a transaction. Not yet implemented.
count
Get count of records in cluster specified by clusterName. Returns int
or throws an exception.
int $db->count(string $clusterName);
Example:
$newcount = $db->count('default');
Querying server
command
This command provide an ability to execute remote SQL commands. Returns mixed or throws an exception.
mixed $db->command(int $commandMode, string $query[, string $fetchplan]);
Command mode is required to be properly match with query text.
Command modes available are:
OrientDB::COMMAND_QUERY
- for general queries, includingINSERT
,UPDATE
,DELETE
,FIND REFERENCES
, etc.OrientDB::COMMAND_SELECT_SYNC
- only forSELECT
in synchronous modeOrientDB::COMMAND_SELECT_ASYNC
- only forSELECT
in asynchronous modeOrientDB::COMMAND_SELECT_GREMLIN
- only for Gremlin engine, available with OrientDB - Graph Edition only
Fetchplan is used to pre-fetch some records.
Using fetchplan will populate $db->cachedRecords
array as for recordLoad()
.
Default fetchplan is *:0
.
Examples:
$records = $db->command(OrientDB::COMMAND_SELECT_ASYNC, 'select * from city limit 7');
$records = $db->command(OrientDB::COMMAND_SELECT_ASYNC, 'select from city traverse( any() )', '*:-1');
$false = $db->command(OrientDB::COMMAND_SELECT_SYNC, 'select from 11:4 where any() traverse(0,10) (address.city = "Rome")');
$links = $db->command(OrientDB::COMMAND_QUERY, 'find references 14:1');
$record = $db->command(OrientDB::COMMAND_QUERY, 'insert into city (name, country) values ("Potenza", #14:1)');
$updatedCount = $db->command(OrientDB::COMMAND_QUERY, 'update city set name = "Taranto" where name = "Potenza"');
$deletedCount = $this->db->command(OrientDB::COMMAND_QUERY, 'delete from city where name = "Taranto"');
select
Is an alias for command(OrientDB::COMMAND_SELECT_SYNC, string $query).
mixed $db->select(string $query);
Example:
$records = $db->select('select from city traverse( any() )');
selectAsync
Is an alias for command(OrientDB::COMMAND_SELECT_ASYNC, string $query[, string $fetchplan]).
mixed $db->selectAsync(string $query[, string $fetchplan]);
Example:
$records = $db->selectAsync('select * from city limit 7', '*:-1');
query
Is an alias for command(OrientDB::COMMAND_QUERY, string $query).
mixed $db->query(string $query);
Example:
$records = $db->query('insert into city (name, country) values ("Potenza", #14:1) ');
selectGremlin
Is an alias for command(OrientDB::COMMAND_SELECT_GREMLIN, string $query[, string $fetchplan]). Please referrer to Gremlin page. Gremlin support is experimental.
mixed $db->selectGremlin(string $query[, string $fetchplan]);
Example:
$records = $db->selectGremlin('g.V', '*:-1');
Other commands
shutdown
Remotely shutdown OrientDB server. Require valid user name and password. See manual for details. Returns nothing on success or throws an exception.
void $db->shutdown(string $userName, string $password);
Example:
$db->shutdown('root', 'password');
Exceptions list
For present moment OrientDB-PHP is using this list of exceptions:
OrientDBException
- base exception, all exceptions listed below are extending this class. This class used as general error class (in case of OrientDB problems).OrientDBConnectException
- thrown on connect errors.OrientDBDeSerializeException
- thrown on de-serialization errors.OrientDBWrongCommandException
- wrong command sequence exception, for example thrown on callrecordLoad()
if DB is not opened yet.OrientDBWrongParamsException
- wrong params count or other param-related issues.
OrientDBRecord
This class is representing OrientDB record.
Class is holding as much information from OrientDB as we received.
Class fields
Class fields are:
className
- Class name from OrientDB.type
- Document type from OrientDB. E.g.OrientDB::RECORD_TYPE_DOCUMENT
.clusterID
- Cluster ID, from which record was loaded.recordPos
- Record position in cluster.recordID
- Fully qualified record ID in formatclusterID:recordPos
.version
- Document version from OrientDB.content
- Document content as string in OrientDB's representation.data
- placeholder where data, deserialized fromcontent
, is stored. Developer should manipulate this data in applications.
For complete information on fields data types see PHPDoc in class.
At this point some class fields are public. Please, be careful.
However, class fields content
, clusterID
, recordPos
, recordID
and className
are using magic methods. All of them are available for reading, while only fields content
, clusterID
, recordPos
and className
for writing.
OrientDBRecord Class methods
Class methods are:
parse()
- can be called after maximum amount of fields was populated. Parsescontent
and fill up fieldsdata
andclassName
. FieldrecordPos
are filled up automatically on settingrecordID
orclusterID
via magic method__set()
. In general, there is no need to call this method directly from user code, as record content is parsed automatically on request to anydata
orclassName
fields. This is done viaOrientDBRecordData
class. This magic parsing only done once, until newcontent
is assigned.setParsed()
- forces that record was already parsed.__toString()
- serialize back all fields fromdata
. Return a string. Also can be called implicitly as type casting, e.g.(string) $record
.reset()
- fully reset class fields, equals tonew
resetData()
- will reset class data, except forclusterID
andclassName
.
Class is able to parse almost any record format as received from OrientDB server. However, there are some limitations about few Java primitive data types, e.g. short. This is a planned TODO.
OrientDBData Class
This class is used to store deserialized content of record. Deserialization is done "on the fly" while code accessing some of the class fields.
Class OrientDBData
implements Countable
, Iterator
interfaces. As a result, you can use foreach()
loop and count()
:
foreach ($record->data as $key => $value) {
echo $key . '=' . $value . PHP_EOL;
}
echo count($record->data);
Class OrientDBData
contains magic methods __isset()
and __unset()
, so any of class's properties can be checked with isset()
and unsetted with unset()
.
if (isset($reord->data->key)) {
unset($record->data->key);
}
Also, class has method getKeys()
which is similar to array_keys
.
Examples
recordLoad:
$record = $db->recordLoad('12:1', '*:2');
var_dump($record);
will produce
object(OrientDBRecord)#197 (9) {
["className"]=>
string(7) "Address"
["type"]=>
string(1) "d"
["clusterID"]=>
int(12)
["recordPos"]=>
int(1)
["recordID"]=>
string(4) "12:1"
["version"]=>
int(0)
["content"]=>
string(61) "Address@street:"Piazza Navona, 1",type:"Residence",city:#13:0"
["data"]=>
object(stdClass)#172 (3) {
["street"]=>
string(16) "Piazza Navona, 1"
["type"]=>
string(9) "Residence"
["city"]=>
object(OrientDBTypeLink)#195 (1) {
["link":"OrientDBTypeLink":private]=>
string(4) "13:0"
}
}
}
recordCreate
$record = new OrientDBRecord();
$record->data->FirstName = 'Bruce';
$record->data->LastName = 'Wayne';
$record->data->appearance = 1939;
$recordPos = $db->recordCreate($clusterID, (string) $record);
var_dump($db->recordLoad($clusterID . ':' . $recordPos));
will produce
object(OrientDBRecord)#176 (9) {
["className"]=>
NULL
["type"]=>
string(1) "d"
["clusterID"]=>
int(1)
["recordPos"]=>
int(138)
["recordID"]=>
string(5) "1:138"
["version"]=>
int(0)
["content"]=>
string(50) "FirstName:"Bruce",LastName:"Wayne",appearance:1939"
["data"]=>
object(stdClass)#179 (3) {
["FirstName"]=>
string(5) "Bruce"
["LastName"]=>
string(5) "Wayne"
["appearance"]=>
int(1939)
}
}
Datatypes
Due to small quantity of PHP's built-in datatypes, this library is introducing some own datatypes.
OrientDBLink
Used to link records with each other.
Two variants of constructing new instance is available:
OrientDBTypeLink(string $value);
String value can be defined with or without leading hash sign.
OrientDBTypeLink(int $clusterID, int $recordPos);
Example 1: String with hash sign
$link = new OrientDBTypeLink('#100:99');
echo $link . PHP_EOL;
echo $link->getHash() . PHP_EOL;
echo $link->get() . PHP_EOL;
echo $link->clusterID . PHP_EOL;
echo $link->recordPos . PHP_EOL;
Example 2: String without hash sign
$link2 = new OrientDBTypeLink('100:99');
echo $link2 . PHP_EOL;
echo $link2->getHash() . PHP_EOL;
echo $link2->get() . PHP_EOL;
echo $link->clusterID . PHP_EOL;
echo $link->recordPos . PHP_EOL;
Example 3: Two integers
$link3 = new OrientDBTypeLink(100, 99);
echo $link2 . PHP_EOL;
echo $link2->getHash() . PHP_EOL;
echo $link2->get() . PHP_EOL;
echo $link->clusterID . PHP_EOL;
echo $link->recordPos . PHP_EOL;
Output of all these examples would be the same:
#100:99
#100:99
100:99
100
99
OrientDBTypeTime
Used to store OrientDB date format with timestamps.
OrientDBTypeLink(mixed $value);
Example:
$date = new OrientDBTypeDate('1302631023t');
$date2 = new OrientDBTypeDate(1302631023);
echo (string) $date . PHP_EOL;
echo $date->getValue() . PHP_EOL;
echo $date->getTime() . PHP_EOL;
Both $date
and $date2
will output the same:
1302631023t
1302631023t
1302631023
Debugging with OrientDB-PHP
For debug purposes you can enable or disable debug output at anytime.
Example:
$db->DBOpen('demo', 'writer', 'writer');
$recordPos = $db->recordCreate($clusterID, $recordContent);
$this->db->setDebug(true);
$record = $db->recordLoad($clusterID . ':' . $recordPos);
$this->db->setDebug(false);
$result = $db->recordDelete($clusterID . ':' . $recordPos);
The above example will output debug messages only for recordLoad()
to standard output stream (browser or console) in this manner:
0 : 1e 00 00 00 04 00 01 00 00 00 00 00 00 00 8f 00 [................]
10 : 00 00 03 2a 3a 30 [...*:0]
>request_status
0 : 00 [.]
>TransactionID
0 : 00 00 00 04 [....]
>record_status_first
0 : 01 [.]
>record_content
0 : 00 00 00 0c [....]
0 : 74 65 73 74 72 65 63 6f 72 64 3a 30 [testrecord:0]
>record_version
0 : 00 00 00 00 [....]
>record_type
0 : 64 [d]
>record_status_cache
0 : 00 [.]
Planned TODOs
- Full support on Java primitive data types, e.g. short or byte.
- Possible more OOP-style work with OrientDBRecord.
- Possible using libevent for selectAsync().
- Support for async mode for RECORD_CREATE, RECORD_UPDATE, RECORD_DELETE
- Support for converting string
'true'
to actual booleantrue
(and other values) in SQL - Use aliases for query serialization ('q' and 'c') instead of long class names
- Parse special Linkset
- Internally process some OrientDB's exceptions and return false (For example -
DBDelete()
)
Known bugs
- Connecting to OrientDB instance, which is listening 0.0.0.0 (default for OrientDB) can cause errors. Change to 127.0.0.1 in Orient's configuration. See origina issue, see github issue
- Only database with type 'document' is supported right now.
If you found a bug
If you found a bug - feel free to contact me via gitHub, email, or open a new issue.