Skip to content

Commit

Permalink
PHPLIB-1309 Add addSubscriber/removeSubscriber to Client class to eas…
Browse files Browse the repository at this point in the history
…e configuration (#1195)

Co-authored-by: Jeremy Mikola <jmikola@gmail.com>
  • Loading branch information
GromNaN and jmikola committed Nov 23, 2023
1 parent 9f8eac1 commit b455ab7
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,7 @@
# CHANGELOG


## 1.18.0 (unreleased)

* Add `addSubscriber` and `removeSubscriber` methods to the `Client` class to ease dependency injection configuration

@@ -0,0 +1,9 @@
arg_name: param
name: $subscriber
type: :php:`MongoDB\\Driver\\Monitoring\\Subscriber <class.mongodb-driver-monitoring-subscriber>`
description: |
A monitoring event subscriber to register with this Client.
interface: phpmethod
operation: ~
optional: false
...
@@ -0,0 +1,9 @@
arg_name: param
name: $subscriber
type: :php:`MongoDB\\Driver\\Monitoring\\Subscriber <class.mongodb-driver-monitoring-subscriber>`
description: |
A monitoring event subscriber to unregister with this Client.
interface: phpmethod
operation: ~
optional: false
...
2 changes: 2 additions & 0 deletions docs/reference/class/MongoDBClient.txt
Expand Up @@ -30,6 +30,7 @@ Methods

/reference/method/MongoDBClient__construct
/reference/method/MongoDBClient__get
/reference/method/MongoDBClient-addSubscriber
/reference/method/MongoDBClient-createClientEncryption
/reference/method/MongoDBClient-dropDatabase
/reference/method/MongoDBClient-getManager
Expand All @@ -39,6 +40,7 @@ Methods
/reference/method/MongoDBClient-getWriteConcern
/reference/method/MongoDBClient-listDatabaseNames
/reference/method/MongoDBClient-listDatabases
/reference/method/MongoDBClient-removeSubscriber
/reference/method/MongoDBClient-selectCollection
/reference/method/MongoDBClient-selectDatabase
/reference/method/MongoDBClient-startSession
Expand Down
132 changes: 132 additions & 0 deletions docs/reference/method/MongoDBClient-addSubscriber.txt
@@ -0,0 +1,132 @@
================================
MongoDB\\Client::addSubscriber()
================================

.. versionadded:: 1.18

.. default-domain:: mongodb

.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol

Definition
----------

.. phpmethod:: MongoDB\\Client::addSubscriber()

Registers a monitoring event subscriber with this Client. The subscriber
will be notified of all events for this Client.

.. code-block:: php

function addSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber): void

This method has the following parameters:

.. include:: /includes/apiargs/MongoDBClient-method-addSubscriber-param.rst

.. note::

If ``$subscriber`` is already registered with this Client, this function
is a no-op. If ``$subscriber`` is also registered globally, it will still
only be notified once of each event for this Client.

Errors/Exceptions
-----------------

.. include:: /includes/extracts/error-invalidargumentexception.rst

:php:`MongoDB\\Driver\\Exception\\InvalidArgumentException <mongodb-driver-exception-invalidargumentexception>`
if subscriber is a :php:`MongoDB\\Driver\\Monitoring\\LogSubscriber <class.mongodb-driver-monitoring-logsubscriber>`,
since loggers can only be registered globally with
:php:`MongoDB\\Driver\\Monitoring\\addSubscriber <function.mongodb.driver.monitoring.addsubscriber>`.

Example
-------

Create a :phpclass:`MongoDB\\Driver\\Monitoring\\CommandSubscriber` that
logs all events:

.. code-block:: php

<?php

use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
use MongoDB\Driver\Monitoring\CommandFailedEvent;

class LogCommandSubscriber implements CommandSubscriber
{
private $stream;
public function __construct($stream)
{
$this->stream = $stream;
}

public function commandStarted(CommandStartedEvent $event): void
{
fwrite($this->stream, sprintf(
'Started command #%d "%s": %s%s',
$event->getRequestId(),
$event->getCommandName(),
Document::fromPHP($event->getCommand())->toCanonicalExtendedJSON(),
PHP_EOL,
));
}

public function commandSucceeded(CommandSucceededEvent $event): void
{
fwrite($this->stream, sprintf(
'Succeeded command #%d "%s" in %d microseconds: %s%s',
$event->getRequestId(),
$event->getCommandName(),
$event->getDurationMicros(),
json_encode($event->getReply()),
PHP_EOL,
));
}

public function commandFailed(CommandFailedEvent $event): void
{
fwrite($this->stream, sprintf(
'Failed command #%d "%s" in %d microseconds: %s%s',
$event->getRequestId(),
$event->getCommandName(),
$event->getDurationMicros(),
$event->getError()->getMessage(),
PHP_EOL,
));
}
}

The subscriber can then be registered with a Client:

.. code-block:: php

<?php

$client = new MongoDB\Client();
$subscriber = new LogCommandSubscriber(STDERR);

$client->addSubscriber($subscriber);

$client->test->users->insertOne(['username' => 'alice']);

The above code will write the following to stderr output:

.. code-block:: text

Started command #1 "insert": { "insert" : "users", "ordered" : true, "$db" : "test", "lsid" : { "id" : { "$binary" : { "base64" : "dKTBhZD7Qvi0vUhvR58mCA==", "subType" : "04" } } }, "documents" : [ { "username" : "alice", "_id" : { "$oid" : "655d1fca12e81018340a4fc2" } } ] }
Succeeded command #1 "insert" in 876 microseconds: {"n":1,"ok":1}

See Also
--------

- :phpmethod:`MongoDB\\Client::removeSubscriber()`
- :php:`Application Performance Monitoring (APM) <manual/en/mongodb.tutorial.apm>`
- :php:`MongoDB\\Driver\\Manager::addSubscriber() <manual/en/mongodb-driver-manager.addsubscriber>`
- :php:`MongoDB\Driver\Monitoring\CommandSubscriber <manual/en/class.mongodb-driver-monitoring-commandsubscriber>`
46 changes: 46 additions & 0 deletions docs/reference/method/MongoDBClient-removeSubscriber.txt
@@ -0,0 +1,46 @@
================================
MongoDB\\Client::removeSubscriber()
================================

.. versionadded:: 1.18

.. default-domain:: mongodb

.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol

Definition
----------

.. phpmethod:: MongoDB\\Client::removeSubscriber()

Unregisters a monitoring event subscriber with this Client.

.. code-block:: php

function removeSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber): void

This method has the following parameters:

.. include:: /includes/apiargs/MongoDBClient-method-removeSubscriber-param.rst

.. note::

If ``$subscriber`` is not registered with this Client, this function
is a no-op.

Errors/Exceptions
-----------------

.. include:: /includes/extracts/error-invalidargumentexception.rst

See Also
--------

- :phpmethod:`MongoDB\\Client::addSubscriber()`
- :php:`Application Performance Monitoring (APM) <manual/en/mongodb.tutorial.apm>`
- :php:`MongoDB\\Driver\\Manager::removeSubscriber() <manual/en/mongodb-driver-manager.addsubscriber>`
- :php:`MongoDB\Driver\Monitoring\CommandSubscriber <manual/en/class.mongodb-driver-monitoring-commandsubscriber>`
2 changes: 1 addition & 1 deletion examples/command_logger.php
Expand Up @@ -56,7 +56,7 @@ public function commandFailed(CommandFailedEvent $event): void

$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/');

$client->getManager()->addSubscriber(new CommandLogger());
$client->addSubscriber(new CommandLogger());

$collection = $client->test->command_logger;
$collection->drop();
Expand Down
21 changes: 21 additions & 0 deletions src/Client.php
Expand Up @@ -23,6 +23,7 @@
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Monitoring\Subscriber;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Session;
Expand Down Expand Up @@ -163,6 +164,16 @@ public function __toString()
return $this->uri;
}

/**
* Registers a monitoring event subscriber with this Client's Manager
*
* @see Manager::addSubscriber()
*/
final public function addSubscriber(Subscriber $subscriber): void
{
$this->manager->addSubscriber($subscriber);
}

/**
* Returns a ClientEncryption instance for explicit encryption and decryption
*
Expand Down Expand Up @@ -296,6 +307,16 @@ public function listDatabases(array $options = [])
return $operation->execute($server);
}

/**
* Unregisters a monitoring event subscriber with this Client's Manager
*
* @see Manager::removeSubscriber()
*/
final public function removeSubscriber(Subscriber $subscriber): void
{
$this->manager->removeSubscriber($subscriber);
}

/**
* Select a collection.
*
Expand Down
18 changes: 18 additions & 0 deletions tests/ClientFunctionalTest.php
Expand Up @@ -4,7 +4,9 @@

use MongoDB\Client;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Command;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Session;
use MongoDB\Model\DatabaseInfo;
use MongoDB\Model\DatabaseInfoIterator;
Expand Down Expand Up @@ -119,4 +121,20 @@ public function testStartSession(): void
{
$this->assertInstanceOf(Session::class, $this->client->startSession());
}

public function testAddAndRemoveSubscriber(): void
{
$client = new Client(static::getUri());

$addedSubscriber = $this->createMock(CommandSubscriber::class);
$addedSubscriber->expects($this->once())->method('commandStarted');
$client->addSubscriber($addedSubscriber);

$removedSubscriber = $this->createMock(CommandSubscriber::class);
$removedSubscriber->expects($this->never())->method('commandStarted');
$client->addSubscriber($removedSubscriber);
$client->removeSubscriber($removedSubscriber);

$client->getManager()->executeCommand('admin', new Command(['ping' => 1]));
}
}

0 comments on commit b455ab7

Please sign in to comment.