From b1a27b70e928180230ae491736dece5d54cafaea Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 7 Jul 2016 14:07:58 -0400 Subject: [PATCH] [Internal BWC Break] Add better ability to inject namespaces Adds a new `registerNamespace` method to the client builder, and supporting infrastructure to use newly registered namespaces in the client. It was previously possible to do this via the `setEndpoint()` method... but it was very difficult and not pleasant to use. Now, users can implement the `NamespaceBuilderInterface` interface, which is registered into the client build and used to instantiate user-defined namespaces when the client is built. Those namespaces are then automatically used by the client as if they were built-in. Note: namespaces cannot conflict with existing namespaces (if they do, they will never be called since this uses the __call dynamic method, which will defer to explicit method names first). Also note that two registered namespaces with the same name will results in a last-register-wins situation (e.g. they are stored in a map), so avoid conflicts :) --- src/Elasticsearch/Client.php | 25 ++++++- src/Elasticsearch/ClientBuilder.php | 29 +++++++-- .../Namespaces/NamespaceBuilderInterface.php | 37 +++++++++++ .../Tests/RegisteredNamespaceTest.php | 65 +++++++++++++++++++ 4 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/Elasticsearch/Namespaces/NamespaceBuilderInterface.php create mode 100644 tests/Elasticsearch/Tests/RegisteredNamespaceTest.php diff --git a/src/Elasticsearch/Client.php b/src/Elasticsearch/Client.php index 7ca5cac59..5efd3eb99 100644 --- a/src/Elasticsearch/Client.php +++ b/src/Elasticsearch/Client.php @@ -2,6 +2,7 @@ namespace Elasticsearch; +use Elasticsearch\Common\Exceptions\BadMethodCallException; use Elasticsearch\Common\Exceptions\InvalidArgumentException; use Elasticsearch\Common\Exceptions\Missing404Exception; use Elasticsearch\Common\Exceptions\TransportException; @@ -10,6 +11,7 @@ use Elasticsearch\Namespaces\ClusterNamespace; use Elasticsearch\Namespaces\IndicesNamespace; use Elasticsearch\Namespaces\IngestNamespace; +use Elasticsearch\Namespaces\NamespaceBuilderInterface; use Elasticsearch\Namespaces\NodesNamespace; use Elasticsearch\Namespaces\SnapshotNamespace; use Elasticsearch\Namespaces\BooleanRequestWrapper; @@ -74,13 +76,17 @@ class Client /** @var callback */ protected $endpoints; + /** @var NamespaceBuilderInterface[] */ + protected $registeredNamespaces = []; + /** * Client constructor * * @param Transport $transport * @param callable $endpoint + * @param AbstractNamespace[] $registeredNamespaces */ - public function __construct(Transport $transport, callable $endpoint) + public function __construct(Transport $transport, callable $endpoint, array $registeredNamespaces) { $this->transport = $transport; $this->endpoints = $endpoint; @@ -91,6 +97,7 @@ public function __construct(Transport $transport, callable $endpoint) $this->cat = new CatNamespace($transport, $endpoint); $this->ingest = new IngestNamespace($transport, $endpoint); $this->tasks = new TasksNamespace($transport, $endpoint); + $this->registeredNamespaces = $registeredNamespaces; } /** @@ -1298,6 +1305,22 @@ public function tasks() return $this->tasks; } + /** + * Catchall for registered namespaces + * + * @param $name + * @param $arguments + * @return Object + * @throws BadMethodCallException if the namespace cannot be found + */ + public function __call($name, $arguments) + { + if (isset($this->registeredNamespaces[$name])) { + return $this->registeredNamespaces[$name]; + } + throw new BadMethodCallException("Namespace [$name] not found"); + } + /** * @param array $params * @param string $arg diff --git a/src/Elasticsearch/ClientBuilder.php b/src/Elasticsearch/ClientBuilder.php index 3d8557a8e..0914e63c8 100644 --- a/src/Elasticsearch/ClientBuilder.php +++ b/src/Elasticsearch/ClientBuilder.php @@ -10,6 +10,7 @@ use Elasticsearch\Connections\Connection; use Elasticsearch\Connections\ConnectionFactory; use Elasticsearch\Connections\ConnectionFactoryInterface; +use Elasticsearch\Namespaces\NamespaceBuilderInterface; use Elasticsearch\Serializers\SerializerInterface; use Elasticsearch\ConnectionPool\Selectors; use Elasticsearch\Serializers\SmartSerializer; @@ -39,6 +40,9 @@ class ClientBuilder /** @var callback */ private $endpoint; + /** @var NamespaceBuilderInterface[] */ + private $registeredNamespacesBuilders = []; + /** @var ConnectionFactoryInterface */ private $connectionFactory; @@ -230,6 +234,17 @@ public function setEndpoint($endpoint) return $this; } + /** + * @param NamespaceBuilderInterface $namespaceBuilder + * @return $this + */ + public function registerNamespace(NamespaceBuilderInterface $namespaceBuilder) + { + $this->registeredNamespacesBuilders[] = $namespaceBuilder; + + return $this; + } + /** * @param \Elasticsearch\Transport $transport * @return $this @@ -427,7 +442,6 @@ public function build() $this->buildTransport(); if (is_null($this->endpoint)) { - $transport = $this->transport; $serializer = $this->serializer; $this->endpoint = function ($class) use ($serializer) { @@ -440,17 +454,24 @@ public function build() }; } - return $this->instantiate($this->transport, $this->endpoint); + $registeredNamespaces = []; + foreach ($this->registeredNamespacesBuilders as $builder) { + /** @var $builder NamespaceBuilderInterface */ + $registeredNamespaces[$builder->getName()] = $builder->getObject($this->transport, $this->serializer); + } + + return $this->instantiate($this->transport, $this->endpoint, $registeredNamespaces); } /** * @param Transport $transport * @param callable $endpoint + * @param Object[] $registeredNamespaces * @return Client */ - protected function instantiate(Transport $transport, callable $endpoint) + protected function instantiate(Transport $transport, callable $endpoint, array $registeredNamespaces) { - return new Client($transport, $endpoint); + return new Client($transport, $endpoint, $registeredNamespaces); } private function buildLoggers() diff --git a/src/Elasticsearch/Namespaces/NamespaceBuilderInterface.php b/src/Elasticsearch/Namespaces/NamespaceBuilderInterface.php new file mode 100644 index 000000000..7171dbbca --- /dev/null +++ b/src/Elasticsearch/Namespaces/NamespaceBuilderInterface.php @@ -0,0 +1,37 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache2 + * @link http://elastic.co + */ + +namespace Elasticsearch\Namespaces; + + +use Elasticsearch\Serializers\SerializerInterface; +use Elasticsearch\Transport; + +interface NamespaceBuilderInterface +{ + /** + * Returns the name of the namespace. This is what users will call, e.g. the name + * "foo" will be invoked by the user as `$client->foo()` + * @return string + */ + public function getName(); + + /** + * Returns the actual namespace object which contains your custom methods. The transport + * and serializer objects are provided so that your namespace may do whatever custom + * logic is required. + * + * @param Transport $transport + * @param SerializerInterface $serializer + * @return Object + */ + public function getObject(Transport $transport, SerializerInterface $serializer); +} \ No newline at end of file diff --git a/tests/Elasticsearch/Tests/RegisteredNamespaceTest.php b/tests/Elasticsearch/Tests/RegisteredNamespaceTest.php new file mode 100644 index 000000000..f40430f0c --- /dev/null +++ b/tests/Elasticsearch/Tests/RegisteredNamespaceTest.php @@ -0,0 +1,65 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache2 + * @link http://elasticsearch.org + */ +class RegisteredNamespaceTest extends \PHPUnit_Framework_TestCase +{ + public function tearDown() + { + m::close(); + } + + public function testRegisteringNamespace() + { + $builder = new FooNamespaceBuilder(); + $client = ClientBuilder::create()->registerNamespace($builder)->build(); + $this->assertEquals("123", $client->foo()->fooMethod()); + } + + /** + * @expectedException \Elasticsearch\Common\Exceptions\BadMethodCallException + */ + public function testNonExistingNamespace() + { + $builder = new FooNamespaceBuilder(); + $client = ClientBuilder::create()->registerNamespace($builder)->build(); + $this->assertEquals("123", $client->bar()->fooMethod()); + } +} + +class FooNamespaceBuilder implements Elasticsearch\Namespaces\NamespaceBuilderInterface +{ + public function getName() + { + return "foo"; + } + + public function getObject(Transport $transport, SerializerInterface $serializer) + { + return new FooNamespace(); + } +} + +class FooNamespace +{ + public function fooMethod() + { + return "123"; + } +} \ No newline at end of file