From ad6b9fba5151cff12a0e152a2c95539d42ce453e Mon Sep 17 00:00:00 2001 From: rado-h Date: Mon, 3 Sep 2012 14:36:16 +0300 Subject: [PATCH 1/4] Add ssl options --- lib/Everyman/Neo4j/Transport.php | 11 +++++++++-- lib/Everyman/Neo4j/Transport/Curl.php | 5 +++++ lib/Everyman/Neo4j/Transport/Stream.php | 13 +++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/Everyman/Neo4j/Transport.php b/lib/Everyman/Neo4j/Transport.php index f8c708a..00601b9 100644 --- a/lib/Everyman/Neo4j/Transport.php +++ b/lib/Everyman/Neo4j/Transport.php @@ -17,6 +17,7 @@ abstract class Transport protected $path = '/db/data'; protected $username = null; protected $password = null; + protected $sslOptions = array(); protected $handle = null; @@ -147,16 +148,22 @@ public function setAuth($username=null, $password=null) } /** - * Turn HTTPS on or off + * Turn HTTPS on or off and adds ssl specific options * * Returns this Trnasport object * * @param boolean $useHttps + * @param array $options * @return Transport */ - public function useHttps($useHttps=true) + public function useHttps($useHttps=true, array $options=array()) { $this->scheme = $useHttps ? 'https' : 'http'; + + //add ssl options + if ($this->scheme == "https" && !empty($options)) { + $this->sslOptions = $options; + } return $this; } } diff --git a/lib/Everyman/Neo4j/Transport/Curl.php b/lib/Everyman/Neo4j/Transport/Curl.php index 8b72e10..bff6127 100644 --- a/lib/Everyman/Neo4j/Transport/Curl.php +++ b/lib/Everyman/Neo4j/Transport/Curl.php @@ -79,6 +79,11 @@ public function makeRequest($method, $path, $data=array()) break; } + //add options for the ssl connection + if ($this->scheme == "https" && !empty($this->sslOptions)) { + $options = array_replace($this->sslOptions, $options); + } + $ch = $this->getHandle(); curl_setopt_array($ch, $options); diff --git a/lib/Everyman/Neo4j/Transport/Stream.php b/lib/Everyman/Neo4j/Transport/Stream.php index c295ea3..779cc50 100644 --- a/lib/Everyman/Neo4j/Transport/Stream.php +++ b/lib/Everyman/Neo4j/Transport/Stream.php @@ -16,7 +16,7 @@ public function makeRequest($method, $path, $data=array()) $url = $this->getEndpoint().$path; $context_options = array ( - $this->scheme => array ( + 'http' => array ( 'method' => 'GET', 'ignore_errors' => true, 'header'=> @@ -38,12 +38,17 @@ public function makeRequest($method, $path, $data=array()) case self::POST : case self::PUT : $dataString = $this->encodeData($data); - $context_options[$this->scheme]['method'] = $method; - $context_options[$this->scheme]['content'] = $dataString; - $context_options[$this->scheme]['header'] .= 'Context-Length: ' . strlen($dataString) . "\r\n"; + $context_options['http']['method'] = $method; + $context_options['http']['content'] = $dataString; + $context_options['http']['header'] .= 'Context-Length: ' . strlen($dataString) . "\r\n"; break; } + //add options for the ssl connection + if ($this->scheme == 'https' && !empty($this->sslOptions)) { + $context_options['ssl'] = $this->sslOptions; + } + $context = stream_context_create($context_options); $response = file_get_contents($url, false, $context); // $http_response_header is set by file_get_contents with the http:// wrapper From f1e2f6e3fc9867976b15c06af44a0dc66ca5ff74 Mon Sep 17 00:00:00 2001 From: rado-h Date: Thu, 20 Sep 2012 21:15:02 +0300 Subject: [PATCH 2/4] Add dependency injector --- lib/Everyman/Neo4j/Bootstrap.php | 44 ++++++++ lib/Everyman/Neo4j/Client.php | 24 ++-- .../Neo4j/Command/ExecuteCypherQuery.php | 5 +- lib/Everyman/Neo4j/DI.php | 103 ++++++++++++++++++ lib/Everyman/Neo4j/Transport.php | 11 +- lib/Everyman/Neo4j/Transport/Curl.php | 10 +- lib/Everyman/Neo4j/Transport/Stream.php | 8 +- tests/bootstrap.php | 3 + tests/unit/lib/Everyman/Neo4j/ClientTest.php | 49 ++++++--- tests/unit/lib/Everyman/Neo4j/DITest.php | 64 +++++++++++ 10 files changed, 274 insertions(+), 47 deletions(-) create mode 100644 lib/Everyman/Neo4j/Bootstrap.php create mode 100644 lib/Everyman/Neo4j/DI.php create mode 100644 tests/unit/lib/Everyman/Neo4j/DITest.php diff --git a/lib/Everyman/Neo4j/Bootstrap.php b/lib/Everyman/Neo4j/Bootstrap.php new file mode 100644 index 0000000..267e1ac --- /dev/null +++ b/lib/Everyman/Neo4j/Bootstrap.php @@ -0,0 +1,44 @@ + array( + "verify_peer" => true, + "allow_self_signed" => false + )) +); \ No newline at end of file diff --git a/lib/Everyman/Neo4j/Client.php b/lib/Everyman/Neo4j/Client.php index a24230c..d6e33d5 100644 --- a/lib/Everyman/Neo4j/Client.php +++ b/lib/Everyman/Neo4j/Client.php @@ -30,27 +30,18 @@ class Client */ public function __construct($transport=null, $port=7474) { + try { if ($transport === null) { - $transport = new Transport\Curl(); + $transport = DI::resolve("transport"); } elseif (is_string($transport)) { - $transport = new Transport\Curl($transport, $port); + $transport = DI::resolve("transport", array($transport, $port)); } } catch (Exception $e) { - if ($transport === null) { - $transport = new Transport\Stream(); - } elseif (is_string($transport)) { - $transport = new Transport\Stream($transport, $port); - } + throw new Exception("Could not create transport object!"); } $this->setTransport($transport); - $this->setNodeFactory(function (Client $client, $properties=array()) { - return new Node($client); - }); - $this->setRelationshipFactory(function (Client $client, $properties=array()) { - return new Relationship($client); - }); } /** @@ -431,8 +422,8 @@ public function loadRelationship(Relationship $rel) */ public function makeNode($properties=array()) { - $nodeFactory = $this->nodeFactory; - $node = $nodeFactory($this, $properties); + + $node = DI::resolve("Node", array($this, $properties)); if (!($node instanceof Node)) { throw new Exception('Node factory did not return a Node object.'); } @@ -447,8 +438,7 @@ public function makeNode($properties=array()) */ public function makeRelationship($properties=array()) { - $relFactory = $this->relFactory; - $rel = $relFactory($this, $properties); + $rel = DI::resolve("Relationship", array($this, $properties)); if (!($rel instanceof Relationship)) { throw new Exception('Relationship factory did not return a Relationship object.'); } diff --git a/lib/Everyman/Neo4j/Command/ExecuteCypherQuery.php b/lib/Everyman/Neo4j/Command/ExecuteCypherQuery.php index 5d6e30e..40aa4a2 100644 --- a/lib/Everyman/Neo4j/Command/ExecuteCypherQuery.php +++ b/lib/Everyman/Neo4j/Command/ExecuteCypherQuery.php @@ -6,7 +6,8 @@ Everyman\Neo4j\Command, Everyman\Neo4j\Client, Everyman\Neo4j\Cypher\Query, - Everyman\Neo4j\Query\ResultSet; + Everyman\Neo4j\Query\ResultSet, + Everyman\Neo4j\DI; /** * Perform a query using the Cypher query language and return the results @@ -68,7 +69,7 @@ protected function getPath() throw new Exception('Cypher unavailable'); } - return preg_replace('/^.+\/db\/data/', '', $url); + return preg_replace('/^.+' . preg_quote(DI::resolve("dbPath"), "/") .'/', '', $url); } /** diff --git a/lib/Everyman/Neo4j/DI.php b/lib/Everyman/Neo4j/DI.php new file mode 100644 index 0000000..895dba1 --- /dev/null +++ b/lib/Everyman/Neo4j/DI.php @@ -0,0 +1,103 @@ +host = $host; $this->port = $port; + if (DI::isRegistered("dbPath")) { + $this->path = DI::resolve("dbPath"); + } } /** @@ -156,14 +158,9 @@ public function setAuth($username=null, $password=null) * @param array $options * @return Transport */ - public function useHttps($useHttps=true, array $options=array()) + public function useHttps($useHttps=true) { $this->scheme = $useHttps ? 'https' : 'http'; - - //add ssl options - if ($this->scheme == "https" && !empty($options)) { - $this->sslOptions = $options; - } return $this; } } diff --git a/lib/Everyman/Neo4j/Transport/Curl.php b/lib/Everyman/Neo4j/Transport/Curl.php index bff6127..7cc73c7 100644 --- a/lib/Everyman/Neo4j/Transport/Curl.php +++ b/lib/Everyman/Neo4j/Transport/Curl.php @@ -79,9 +79,13 @@ public function makeRequest($method, $path, $data=array()) break; } - //add options for the ssl connection - if ($this->scheme == "https" && !empty($this->sslOptions)) { - $options = array_replace($this->sslOptions, $options); + //additional curl options + if (\Everyman\Neo4j\DI::isRegistered("curlOptions")) { + $additionalOptions = \Everyman\Neo4j\DI::resolve("curlOptions"); + if (is_array($additionalOptions)) { + $options = array_replace($options, $additionalOptions); + } + unset($additionalOptions); } $ch = $this->getHandle(); diff --git a/lib/Everyman/Neo4j/Transport/Stream.php b/lib/Everyman/Neo4j/Transport/Stream.php index 779cc50..3b21688 100644 --- a/lib/Everyman/Neo4j/Transport/Stream.php +++ b/lib/Everyman/Neo4j/Transport/Stream.php @@ -45,8 +45,12 @@ public function makeRequest($method, $path, $data=array()) } //add options for the ssl connection - if ($this->scheme == 'https' && !empty($this->sslOptions)) { - $context_options['ssl'] = $this->sslOptions; + if (\Everyman\Neo4j\DI::isRegistered("httpStreamOptions")) { + $streamOptions = \Everyman\Neo4j\DI::resolve("httpStreamOptions"); + if (is_array($streamOptions)) { + $context_options = array_replace_recursive($context_options, $streamOptions); + } + unset($streamOptions); } $context = stream_context_create($context_options); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 09ac34d..77be24b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,3 +14,6 @@ function loaderTestAutoloader($sClass) spl_autoload_register('loaderTestAutoloader'); error_reporting(-1); ini_set('display_errors', 1); + + +include __DIR__ . "/../lib/Everyman/Neo4j/Bootstrap.php"; \ No newline at end of file diff --git a/tests/unit/lib/Everyman/Neo4j/ClientTest.php b/tests/unit/lib/Everyman/Neo4j/ClientTest.php index d5c8b1c..b430829 100644 --- a/tests/unit/lib/Everyman/Neo4j/ClientTest.php +++ b/tests/unit/lib/Everyman/Neo4j/ClientTest.php @@ -969,7 +969,7 @@ public function testGetReferenceNode_Found_ReturnsNode() public function testNodeFactory_SetNodeFactory_ReturnsNodeFromFactory() { - $this->client->setNodeFactory(function (Client $client, $properties=array()) { + DI::register("Node", function (Client $client, $properties=array()) { return new NodeFactoryTestClass_ClientTest($client); }); @@ -977,15 +977,9 @@ public function testNodeFactory_SetNodeFactory_ReturnsNodeFromFactory() $this->assertInstanceOf('Everyman\Neo4j\NodeFactoryTestClass_ClientTest', $node); } - public function testNodeFactory_SetNodeFactory_NotCallable_ThrowsException() - { - $this->setExpectedException('Everyman\Neo4j\Exception'); - $this->client->setNodeFactory('bar'); - } - public function testNodeFactory_NodeFactoryReturnsNotNode_ThrowsException() { - $this->client->setNodeFactory(function (Client $client, $properties=array()) { + DI::register("Node", function (Client $client, $properties=array()) { return new \stdClass(); }); @@ -995,7 +989,7 @@ public function testNodeFactory_NodeFactoryReturnsNotNode_ThrowsException() public function testRelationshipFactory_SetRelationshipFactory_ReturnsRelationshipFromFactory() { - $this->client->setRelationshipFactory(function (Client $client, $properties=array()) { + DI::register("Relationship", function (Client $client, $properties=array()) { return new RelFactoryTestClass_ClientTest($client); }); @@ -1003,21 +997,44 @@ public function testRelationshipFactory_SetRelationshipFactory_ReturnsRelationsh $this->assertInstanceOf('Everyman\Neo4j\RelFactoryTestClass_ClientTest', $rel); } - public function testRelationshipFactory_SetRelationshipFactory_NotCallable_ThrowsException() - { - $this->setExpectedException('Everyman\Neo4j\Exception'); - $this->client->setRelationshipFactory('bar'); - } - public function testRelationshipFactory_RelationshipFactoryReturnsNotRelationship_ThrowsException() { - $this->client->setRelationshipFactory(function (Client $client, $properties=array()) { + DI::register("Relationship", function (Client $client, $properties=array()) { return new \stdClass(); }); $this->setExpectedException('Everyman\Neo4j\Exception'); $rel = $this->client->makeRelationship(); } + + public function tearDown() + { + DI::register( + "Node", + function (Client $client, $properties=array()) { + return new Node($client); + } + ); + + DI::register( + "Relationship", + function (Client $client, $properties=array()) { + return new Relationship($client); + } + ); + + DI::unregister("transport"); + DI::register( + "transport", + function($host='localhost', $port=7474) { + if (extension_loaded("curl")) { + return new \Everyman\Neo4j\Transport\Curl($host, $port); + } + return new \Everyman\Neo4j\Transport\Stream($host, $port); + }, + true + ); + } } class NodeFactoryTestClass_ClientTest extends Node {} diff --git a/tests/unit/lib/Everyman/Neo4j/DITest.php b/tests/unit/lib/Everyman/Neo4j/DITest.php new file mode 100644 index 0000000..34b49aa --- /dev/null +++ b/tests/unit/lib/Everyman/Neo4j/DITest.php @@ -0,0 +1,64 @@ +assertEquals(DI::resolve("testScalar"), 42); + DI::unregister("testScalar"); + } + + public function testRegisterClosure() + { + DI::register("testClosure", function() { + return 42; + }); + + $this->assertEquals(DI::resolve("testClosure"), 42); + DI::unregister("testClosure"); + + DI::register("testClosureWithParams", function ($a, $b) { + return $a+$b; + }); + + $this->assertEquals(DI::resolve("testClosureWithParams", array(2,40)), 42); + DI::unregister("testClosureWithParams"); + } + + public function testSingleton() + { + DI::register("testSingleton", function() {return new \StdClass();}, true); + + $singleton1 = DI::resolve("testSingleton"); + $singleton1->test = 42; + + $singleton2 = DI::resolve("testSingleton"); + $this->assertEquals($singleton2->test, 42); + + $singleton2->test++; + $this->assertEquals($singleton1->test, 43); + + DI::unregister("testSingleton"); + } + + public function testUnregister() + { + DI::register("testScalar", 42); + $this->assertTrue(DI::isRegistered("testScalar")); + + DI::unregister("testScalar"); + $this->assertFalse(DI::isRegistered("testScalar")); + + } + + public function testExceptionForUnexistingEntry() + { + $this->setExpectedException('Everyman\Neo4j\Exception'); + DI::resolve("unexistingEntry"); + } + +} \ No newline at end of file From 134fe8d3ad51fa5cd39996c4e78647ec3a4c35d7 Mon Sep 17 00:00:00 2001 From: rado-h Date: Thu, 15 Nov 2012 12:07:17 +0200 Subject: [PATCH 3/4] make the cache keys for nodes and relationships configurable --- lib/Everyman/Neo4j/Cache/EntityCache.php | 27 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/Everyman/Neo4j/Cache/EntityCache.php b/lib/Everyman/Neo4j/Cache/EntityCache.php index 59390cc..40cf0ff 100644 --- a/lib/Everyman/Neo4j/Cache/EntityCache.php +++ b/lib/Everyman/Neo4j/Cache/EntityCache.php @@ -6,7 +6,8 @@ Everyman\Neo4j\PropertyContainer, Everyman\Neo4j\Node, Everyman\Neo4j\Relationship, - Everyman\Neo4j\Cache; + Everyman\Neo4j\Cache, + Everyman\Neo4j\DI; /** * Store and retrieve cached entities without hitting the server @@ -52,7 +53,7 @@ public function getCachedEntity($id, $type) throw new Exception('Unknown entity type: '.$type); } - $entity = $this->getCache()->get("{$type}-{$id}"); + $entity = $this->getCache()->get("{$this->resolveCacheKey($type)}-{$id}"); if ($entity) { $entity->setClient($this->client); } @@ -98,15 +99,31 @@ protected function getCache() * Determine the cache key used to retrieve the given entity from the cache * * @param PropertyContainer $entity - * @return string + * @return string */ protected function getEntityCacheKey(PropertyContainer $entity) { if ($entity instanceof Node) { - return 'node-'.$entity->getId(); + return $this->resolveCacheKey('node') . '-'.$entity->getId(); } else if ($entity instanceof Relationship) { - return 'relationship-'.$entity->getId(); + return $this->resolveCacheKey('relationship') . '-'.$entity->getId(); } throw new Exception('Unknown entity type: '.get_class($entity)); } + + /** + * Resolves cache key by given type. Checks if there is registered + * cache prefix for this type and returns it, otherwise it returns + * the type itself as a key + * @param string $type + * @return string + */ + protected function resolveCacheKey($type) + { + if (DI::isRegistered($type . "CachePrefix")) { + return DI::resolve($type . "CachePrefix"); + } + + return $type; + } } From ad660ac245618f192b88ec79f9509ff882de103b Mon Sep 17 00:00:00 2001 From: rado-h Date: Sun, 10 Feb 2013 22:15:32 +0200 Subject: [PATCH 4/4] Implement sleep method for the property container class --- lib/Everyman/Neo4j/PropertyContainer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Everyman/Neo4j/PropertyContainer.php b/lib/Everyman/Neo4j/PropertyContainer.php index ce8f6b8..2351ac5 100644 --- a/lib/Everyman/Neo4j/PropertyContainer.php +++ b/lib/Everyman/Neo4j/PropertyContainer.php @@ -42,6 +42,11 @@ public function __isset($key) { return array_key_exists($key, $this->properties); } + + public function __sleep() + { + return array('id', 'properties', 'lazyLoad', 'loaded'); + } /** * Delete this entity