Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ Cassandra client library for PHP
<a href="https://scrutinizer-ci.com/g/duoshuo/php-cassandra/"><img src="https://scrutinizer-ci.com/g/duoshuo/php-cassandra/badges/quality-score.png?b=master" /></a>
<a href="https://scrutinizer-ci.com/g/duoshuo/php-cassandra/"><img src="https://scrutinizer-ci.com/g/duoshuo/php-cassandra/badges/build.png?b=master" /></a>

Cassandra client library for PHP, which support Protocol v3 (Cassandra 2.1) and asynchronous request
Cassandra client library for PHP, which support Protocol v4 (Cassandra 4) and asynchronous request

## Features
* Using Protocol v3 (Cassandra 2.1)
* Using Protocol v4 (Cassandra 4)
* Support ssl/tls with stream transport layer
* Support asynchronous and synchronous request
* Support for logged, unlogged and counter batches
Expand Down Expand Up @@ -233,6 +233,18 @@ All types are supported.
// Inet
new Cassandra\Type\Inet('127.0.0.1');

// Date
new Cassandra\Type\Date(date('Y-m-d', strtotime('now')));

// Time
new Cassandra\Type\Time(date('H:i:s', strtotime('now')));

// Smallint
new Cassandra\Type\SmallInt(32700);

// Tinyint
new Cassandra\Type\TinyInt(255);

// Int
new Cassandra\Type\PhpInt(1);

Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "duoshuo/php-cassandra",
"description": "Cassandra client library for PHP, which support Protocol v3 and asynchronous request",
"description": "Cassandra client library for PHP, which support Protocol v4 and asynchronous request",
"minimum-stability": "stable",
"keywords" : [
"cassandra",
Expand All @@ -18,6 +18,10 @@
{
"name": "Evseev Nikolay",
"email": "[email protected]"
},
{
"name": "Ciaran Moore",
"email": "[email protected]"
}
],
"require": {
Expand Down
4 changes: 2 additions & 2 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Connection {
* @var array
*/
protected $_options = [
'CQL_VERSION' => '3.0.0'
'CQL_VERSION' => '4.0.0'
];

/**
Expand Down Expand Up @@ -179,7 +179,7 @@ protected function _getResponse() {

return $response;
default:
throw new Exception('php-cassandra supports CQL binary protocol v3 only, please upgrade your Cassandra to 2.1 or later.');
throw new Exception('php-cassandra supports CQL binary protocol v3+ only, please upgrade your Cassandra to 2.1 or later.');
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/Request/Startup.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ class Startup extends Request{
* different from the protocol version.
* - "COMPRESSION": the compression algorithm to use for frames (See section 5).
* This is optional, if not specified no compression will be used.
* - "NO_COMPACT": whether or not connection has to be established in compatibility
* mode. This mode will make all Thrift and Compact Tables to be exposed as if
* they were CQL Tables. This is optional; if not specified, the option will
* not be used.
* - "THROW_ON_OVERLOAD": In case of server overloaded with too many requests, by
* default the server puts back pressure on the client connection. Instead, the server
* can send an OverloadedException error message back to the client if this option is
* set to true.
*
* @param array $options
*/
Expand Down
205 changes: 121 additions & 84 deletions src/Response/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class Error extends Response {
const TRUNCATE_ERROR = 0x1003;
const WRITE_TIMEOUT = 0x1100;
const READ_TIMEOUT = 0x1200;
const READ_FAILURE = 0x1300;
const FUNCTION_FAILURE = 0x1400;
const WRITE_FAILURE = 0x1500;
const SYNTAX_ERROR = 0x2000;
const UNAUTHORIZED = 0x2100;
const INVALID = 0x2200;
Expand All @@ -27,95 +30,129 @@ class Error extends Response {
*
* @return array
*/
public function getData() {
$this->_stream->offset(0);
$data = [];
$data['code'] = $this->_stream->readInt();

switch($data['code']){
case self::SERVER_ERROR:
$data['message'] = "Server error: " . $this->_stream->readString();
break;

case self::PROTOCOL_ERROR:
$data['message'] = "Protocol error: " . $this->_stream->readString();
break;

case self::BAD_CREDENTIALS:
$data['message'] = "Bad credentials: " . $this->_stream->readString();
break;

case self::UNAVAILABLE_EXCEPTION:
$data['message'] = "Unavailable exception. Error data: " . var_export([
public function getData() {
$this->_stream->offset(0);
$data = [];
$data['code'] = $this->_stream->readInt();

switch($data['code']){
case self::SERVER_ERROR:
$data['message'] = "Server error - Something unexpected happened. This indicates a server-side bug: " . $this->_stream->readString();
break;

case self::PROTOCOL_ERROR:
$data['message'] = "Protocol error - Some client message triggered a protocol violation (for instance a QUERY message is sent before a STARTUP one has been sent): " . $this->_stream->readString();
break;

case self::BAD_CREDENTIALS:
$data['message'] = "Authentication error - Authentication was required and failed. The possible reason for failing depends on the authenticator in use, which may or may not include more detail in the accompanying error message: " . $this->_stream->readString();
break;
// ERROR message body: <cl><required><alive>
case self::UNAVAILABLE_EXCEPTION:
$data['message'] = "Unavailable exception. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt()
], true);
break;

case self::OVERLOADED:
$data['message'] = "Overloaded: " . $this->_stream->readString();
break;

case self::IS_BOOTSTRAPPING:
$data['message'] = "Is_bootstrapping: " . $this->_stream->readString();
break;

case self::TRUNCATE_ERROR:
$data['message'] = "Truncate_error: " . $this->_stream->readString();
break;

case self::WRITE_TIMEOUT:
$data['message'] = "Write_timeout. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'writeType' => $this->_stream->readString()
], true);
break;

case self::READ_TIMEOUT:
$data['message'] = "Read_timeout. Error data: " . var_export([
'replica' => $this->_stream->readInt()
], true);
break;

case self::OVERLOADED:
$data['message'] = "Overloaded - the request cannot be processed because the coordinator node is overloaded: " . $this->_stream->readString();
break;

case self::IS_BOOTSTRAPPING:
$data['message'] = "Is_bootstrapping - the request was a read request but the coordinator node is bootstrapping: " . $this->_stream->readString();
break;

case self::TRUNCATE_ERROR:
$data['message'] = "Truncate_error - Error during a truncation error: " . $this->_stream->readString();
break;
// ERROR message body: <cl><received><blockfor><writeType>
case self::WRITE_TIMEOUT:
$data['message'] = "Write_timeout - Timeout exception during a write request. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'dataPresent' => $this->_stream->readChar()
], true);
break;

case self::SYNTAX_ERROR:
$data['message'] = "Syntax_error: " . $this->_stream->readString();
break;

case self::UNAUTHORIZED:
$data['message'] = "Unauthorized: " . $this->_stream->readString();
break;

case self::INVALID:
$data['message'] = "Invalid: " . $this->_stream->readString();
break;

case self::CONFIG_ERROR:
$data['message'] = "Config_error: " . $this->_stream->readString();
break;

case self::ALREADY_EXIST:
$data['message'] = "Already_exists: " . $this->_stream->readString();
break;

case self::UNPREPARED:
$data['message'] = "Unprepared: " . $this->_stream->readShort();
break;

default:
$data['message'] = 'Unknown error';
}

return $data;
}
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'writeType' => $this->_stream->readString()
], true);
break;
// ERROR message body: <cl><received><blockfor><data_present>
case self::READ_TIMEOUT:
$data['message'] = "Read_timeout - Timeout exception during a read request. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'dataPresent' => $this->_stream->readChar()
], true);
break;
// ERROR message body: <cl><received><blockfor><numfailures><data_present>
case self::READ_FAILURE:
$data['message'] = "Read_failure - A non-timeout exception during a read request. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'numfailures' => $this->_stream->readInt(),
'dataPresent' => $this->_stream->readChar()
], true);
break;
// ERROR message body: <keyspace><function><arg_types>
case self::FUNCTION_FAILURE:
$data['message'] = "Function_failure - A (user defined) function failed during execution. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'keyspace' => $this->_stream->readString(),
'function' => $this->_stream->readString(),
'arg_types' => $this->_stream->readString(),
], true);
break;
// ERROR message body: <cl><received><blockfor><numfailures><write_type>
case self::WRITE_FAILURE:
$data['message'] = "Write_failure - A non-timeout exception during a write request. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
'replica' => $this->_stream->readInt(),
'writeType' => $this->_stream->readString()
], true);
break;

case self::SYNTAX_ERROR:
$data['message'] = "Syntax_error - The submitted query has a syntax error: " . $this->_stream->readString();
break;

case self::UNAUTHORIZED:
$data['message'] = "Unauthorized - The logged user doesn't have the right to perform the query: " . $this->_stream->readString();
break;

case self::INVALID:
$data['message'] = "Invalid - The query is syntactically correct but invalid: " . $this->_stream->readString();
break;

case self::CONFIG_ERROR:
$data['message'] = "Config_error - The query is invalid because of some configuration issue: " . $this->_stream->readString();
break;
// ERROR message body: <ks><table>
case self::ALREADY_EXIST:
$data['message'] = "Already_exists - The query attempted to create a keyspace or a table that was already existing: " . var_export([
'error'=>$this->_stream->readString(),
'keyspace' => $this->_stream->readString(),
'table' => $this->_stream->readString()
], true);
break;

case self::UNPREPARED:
$data['message'] = "Unprepared - Can be thrown while a prepared statement tries to be executed if the provided prepared statement ID is not known by this host: " . $this->_stream->readShort();
break;

default:
$data['message'] = 'Unknown error';
}

return $data;
}

/**
*
Expand Down
10 changes: 8 additions & 2 deletions src/Type/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ abstract class Base{
const DOUBLE = 0x0007;
const FLOAT = 0x0008;
const INT = 0x0009;
const TEXT = 0x000A; // deprecated in Protocol v3
const TIMESTAMP = 0x000B;
const UUID = 0x000C;
const VARCHAR = 0x000D;
const VARINT = 0x000E;
const TIMEUUID = 0x000F;
const INET = 0x0010;
const DATE = 0x0011;
const TIME = 0x0012;
const SMALLINT = 0x0013;
const TINYINT = 0x0014;
const COLLECTION_LIST = 0x0020;
const COLLECTION_MAP = 0x0021;
const COLLECTION_SET = 0x0022;
Expand All @@ -29,7 +32,6 @@ abstract class Base{
public static $typeClassMap = [
self::ASCII => 'Cassandra\Type\Ascii',
self::VARCHAR => 'Cassandra\Type\Varchar',
self::TEXT => 'Cassandra\Type\Varchar', // deprecated in Protocol v3
self::VARINT => 'Cassandra\Type\Varint',
self::BIGINT => 'Cassandra\Type\Bigint',
self::COUNTER => 'Cassandra\Type\Counter',
Expand All @@ -43,6 +45,10 @@ abstract class Base{
self::UUID => 'Cassandra\Type\Uuid',
self::TIMEUUID => 'Cassandra\Type\Timeuuid',
self::INET => 'Cassandra\Type\Inet',
self::DATE => 'Cassandra\Type\Date',
self::TIME => 'Cassandra\Type\Time',
self::SMALLINT => 'Cassandra\Type\SmallInt',
self::TINYINT => 'Cassandra\Type\TinyInt',
self::COLLECTION_LIST => 'Cassandra\Type\CollectionList',
self::COLLECTION_SET => 'Cassandra\Type\CollectionSet',
self::COLLECTION_MAP => 'Cassandra\Type\CollectionMap',
Expand Down
36 changes: 36 additions & 0 deletions src/Type/Date.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Cassandra\Type;

class Date extends Base {

/**
* @param string $dateString Format: 'YYYY-MM-DD'
* @throws Exception
*/
public function __construct($dateString = null){
if ($dateString === null)
return;

$parsedDate = strtotime($dateString);
if ($parsedDate === false)
throw new Exception('Invalid date format.');

$this->_value = $parsedDate;
}

public static function binary($dateString){
$parsedDate = strtotime($dateString);
return pack('N', $parsedDate);
}

/**
* @return string
*/
public static function parse($binary){
$unpacked = unpack('N', $binary);
return date('Y-m-d', $unpacked[1]);
}
}
Explanation:

Loading