diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..88e99d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+vendor
+composer.lock
\ No newline at end of file
diff --git a/composer.json b/composer.json
index aa20e71..13654ee 100755
--- a/composer.json
+++ b/composer.json
@@ -11,12 +11,23 @@
{
"name": "Evseev Nikolay",
"email": "evseevnn@gmail.com"
+ },
+ {
+ "name": "Aleksandar Babic",
+ "email": "salebab@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
"autoload": {
"psr-4": { "Cassandra\\": "src/" }
+ },
+
+ "autoload-dev" : {
+ "psr-4": { "Cassandra\\": "tests/"}
}
}
diff --git a/phpunit.php b/phpunit.php
new file mode 100644
index 0000000..39a1434
--- /dev/null
+++ b/phpunit.php
@@ -0,0 +1,5 @@
+
+
+
+ ./tests/
+
+
+
+
+ src
+
+ vendor
+
+
+
+
\ No newline at end of file
diff --git a/src/Connection/Node.php b/src/Connection/Node.php
index aefdbe0..b09c0de 100644
--- a/src/Connection/Node.php
+++ b/src/Connection/Node.php
@@ -45,25 +45,20 @@ public function __construct($options) {
}
/**
- *
+ * Create connection and return socket resource
+ *
* @throws Exception
* @return resource
*/
public function getConnection() {
- if (!empty($this->socket)) return $this->socket;
- $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
-
- if ($this->socket === false)
- throw new Exception(socket_strerror(socket_last_error()));
-
- socket_set_option($this->socket, getprotobyname('TCP'), TCP_NODELAY, 1);
-
- foreach($this->_options['socket'] as $optname => $optval)
- socket_set_option($this->socket, SOL_SOCKET, $optname, $optval);
-
- if (!socket_connect($this->socket, $this->_options['host'], $this->_options['port']))
- throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}");
+ if(!$this->isConnected()) {
+ try {
+ $this->connect();
+ } catch(\Exception $e) {
+ throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}. Reason: ". $e->getMessage(), 0, $e);
+ }
+ }
return $this->socket;
}
@@ -71,7 +66,85 @@ public function getConnection() {
/**
* @return array
*/
- public function getOptions() {
+ public function getOptions()
+ {
return $this->_options;
}
+
+ /**
+ * Connect to host/port
+ *
+ * @throws Exception
+ */
+ public function connect()
+ {
+ $this->createConnection();
+ $this->setConnectionOptions();
+
+ $result = @socket_connect($this->socket, $this->_options['host'], $this->_options['port']);
+
+ if($result === false) {
+ throw new Exception("Unable to connect: " . socket_strerror(socket_last_error($this->socket)));
+ }
+ }
+
+ /**
+ * Disconnect
+ */
+ public function disconnect()
+ {
+ if(is_resource($this->socket)) {
+ @socket_close($this->socket);
+ }
+
+ $this->socket = null;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isConnected()
+ {
+ return is_resource($this->socket);
+ }
+
+
+ /**
+ * @throws Exception
+ */
+ private function createConnection()
+ {
+ $this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+
+ if($this->socket === false) {
+ throw new Exception("Unable to create socket: ". socket_strerror(socket_last_error()));
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function setConnectionOptions()
+ {
+ $this->setConnectionOption(getprotobyname('tcp'), TCP_NODELAY, 1);
+
+ foreach($this->_options['socket'] as $name => $value) {
+ $this->setConnectionOption(SOL_SOCKET, $name, $value);
+ }
+ }
+
+ /**
+ * @param int $level
+ * @param int $name
+ * @param mixed $value
+ * @throws Exception
+ */
+ private function setConnectionOption($level, $name, $value)
+ {
+ $result = @socket_set_option($this->socket, $level, $name, $value);
+
+ if($result === false) {
+ throw new Exception("Unable to set socket option: $level, $name");
+ }
+ }
}
diff --git a/tests/Connection/NodeTest.php b/tests/Connection/NodeTest.php
new file mode 100644
index 0000000..62ba0e7
--- /dev/null
+++ b/tests/Connection/NodeTest.php
@@ -0,0 +1,107 @@
+listen(19042);
+ }
+
+ public static function tearDownAfterClass()
+ {
+ self::$server->shutdown();
+ }
+
+ public function testNewInstance()
+ {
+ $node = new Node("localhost:19042");
+
+ $this->assertInstanceOf('Cassandra\Connection\Node', $node);
+
+ return $node;
+ }
+
+ /**
+ * @param Node $node
+ * @depends testNewInstance
+ */
+ public function testIsConnected(Node $node)
+ {
+ $this->assertFalse($node->isConnected());
+
+ $node->connect();
+
+ $this->assertTrue($node->isConnected());
+ }
+
+
+ /**
+ * @depends testNewInstance
+ * @param Node $node
+ */
+ public function testGetOptions(Node $node)
+ {
+ $this->assertArrayHasKey("host", $node->getOptions());
+ $this->assertArrayHasKey("port", $node->getOptions());
+ }
+
+ /**
+ * @depends testNewInstance
+ * @param Node $node
+ */
+ public function testGetConnectionReturnsResource(Node $node)
+ {
+ $connection = $node->getConnection();
+
+ $this->assertTrue(is_resource($connection));
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function testGetConnectionException()
+ {
+ $node = new Node("invalidhost:9042");
+
+ $this->setExpectedException('Cassandra\Connection\Exception', "invalidhost:9042");
+
+ $node->getConnection();
+ $node->disconnect();
+ }
+
+ public function testInvalidOptions()
+ {
+ $node = new Node([
+ 'host' => 'localhost',
+ 'port' => 19042,
+ 'socket' => [
+ SO_RCVTIMEO => -1,
+ ]
+ ]);
+
+ $this->setExpectedException('Cassandra\Connection\Exception', "Unable to set socket option");
+
+ $node->connect();
+ }
+
+ /**
+ *
+ * @depends testNewInstance
+ * @param Node $node
+ */
+ public function testDisconnect(Node $node)
+ {
+ $node->disconnect();
+
+ $this->assertFalse($node->isConnected());
+ }
+}
diff --git a/tests/Stubs/ListeningServerStub.php b/tests/Stubs/ListeningServerStub.php
new file mode 100644
index 0000000..89fc2dc
--- /dev/null
+++ b/tests/Stubs/ListeningServerStub.php
@@ -0,0 +1,27 @@
+sock = socket_create(AF_INET, SOCK_STREAM, 0);
+
+ // Bind the socket to an address/port
+ if(!socket_bind($this->sock, 'localhost', $port)) {
+ throw new \RuntimeException('Could not bind to address');
+ }
+
+ socket_set_nonblock($this->sock);
+
+ // Start listening for connections
+ socket_listen($this->sock);
+ }
+
+
+ public function shutdown()
+ {
+ socket_shutdown($this->sock);
+ }
+}
\ No newline at end of file
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..6b28a89
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,6 @@
+