Browse Source

Replace deprecated code in MongoDB classes and allow 2.0+ (#1998)

* Remove unused code path in MongoDBFormatter

Monolog already required mongodb/mongodb 1.8+ (and a related ext-mongodb version) so this code path was never used.

* Use Collection::getCollection() from mongodb/mongodb 1.21

The original selectCollection method is deprecated. Since Monolog itself requires PHP 8.1, it should be sane to rely on 1.21+, which shares the same requirement.

* Update class refs for MongoDB extension and library

* Conditionally use Collection getter by version

* Relax PHPLIB version requirement

* Use RequiresPhpExtension attribute to require ext-mongodb

* Import UTCDateTime class and revise var names
Jeremy Mikola 2 months ago
parent
commit
1297c4421b

+ 1 - 1
composer.json

@@ -24,7 +24,7 @@
         "graylog2/gelf-php": "^1.4.2 || ^2.0",
         "guzzlehttp/guzzle": "^7.4.5",
         "guzzlehttp/psr7": "^2.2",
-        "mongodb/mongodb": "^1.8",
+        "mongodb/mongodb": "^1.8 || ^2.0",
         "php-amqplib/php-amqplib": "~2.4 || ^3",
         "php-console/php-console": "^3.1.8",
         "phpstan/phpstan": "^2",

+ 0 - 30
src/Monolog/Formatter/MongoDBFormatter.php

@@ -25,7 +25,6 @@ class MongoDBFormatter implements FormatterInterface
 {
     private bool $exceptionTraceAsString;
     private int $maxNestingLevel;
-    private bool $isLegacyMongoExt;
 
     /**
      * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record->context is 2
@@ -35,8 +34,6 @@ class MongoDBFormatter implements FormatterInterface
     {
         $this->maxNestingLevel = max($maxNestingLevel, 0);
         $this->exceptionTraceAsString = $exceptionTraceAsString;
-
-        $this->isLegacyMongoExt = \extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<=');
     }
 
     /**
@@ -126,34 +123,7 @@ class MongoDBFormatter implements FormatterInterface
     }
 
     protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime
-    {
-        if ($this->isLegacyMongoExt) {
-            return $this->legacyGetMongoDbDateTime($value);
-        }
-
-        return $this->getMongoDbDateTime($value);
-    }
-
-    private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
     {
         return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000));
     }
-
-    /**
-     * This is needed to support MongoDB Driver v1.19 and below
-     *
-     * See https://github.com/mongodb/mongo-php-driver/issues/426
-     *
-     * It can probably be removed in 2.1 or later once MongoDB's 1.2 is released and widely adopted
-     */
-    private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
-    {
-        $milliseconds = floor(((float) $value->format('U.u')) * 1000);
-
-        $milliseconds = (PHP_INT_SIZE === 8) //64-bit OS?
-            ? (int) $milliseconds
-            : (string) $milliseconds;
-
-        return new UTCDateTime($milliseconds);
-    }
 }

+ 4 - 3
src/Monolog/Handler/MongoDBHandler.php

@@ -11,9 +11,10 @@
 
 namespace Monolog\Handler;
 
+use MongoDB\Client;
+use MongoDB\Collection;
 use MongoDB\Driver\BulkWrite;
 use MongoDB\Driver\Manager;
-use MongoDB\Client;
 use Monolog\Level;
 use Monolog\Formatter\FormatterInterface;
 use Monolog\Formatter\MongoDBFormatter;
@@ -34,7 +35,7 @@ use Monolog\LogRecord;
  */
 class MongoDBHandler extends AbstractProcessingHandler
 {
-    private \MongoDB\Collection $collection;
+    private Collection $collection;
 
     private Client|Manager $manager;
 
@@ -50,7 +51,7 @@ class MongoDBHandler extends AbstractProcessingHandler
     public function __construct(Client|Manager $mongodb, string $database, string $collection, int|string|Level $level = Level::Debug, bool $bubble = true)
     {
         if ($mongodb instanceof Client) {
-            $this->collection = $mongodb->selectCollection($database, $collection);
+            $this->collection = method_exists($mongodb, 'getCollection') ? $mongodb->getCollection($database, $collection) : $mongodb->selectCollection($database, $collection);
         } else {
             $this->manager = $mongodb;
             $this->namespace = $database . '.' . $collection;

+ 4 - 9
tests/Monolog/Formatter/MongoDBFormatterTest.php

@@ -16,19 +16,14 @@ use MongoDB\BSON\Regex;
 use MongoDB\BSON\UTCDateTime;
 use Monolog\Level;
 use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\RequiresPhpExtension;
 
 /**
  * @author Florian Plattner <me@florianplattner.de>
  */
+#[RequiresPhpExtension('mongodb')]
 class MongoDBFormatterTest extends \Monolog\Test\MonologTestCase
 {
-    public function setUp(): void
-    {
-        if (!class_exists('MongoDB\BSON\UTCDateTime')) {
-            $this->markTestSkipped('ext-mongodb not installed');
-        }
-    }
-
     public static function constructArgumentProvider()
     {
         return [
@@ -67,7 +62,7 @@ class MongoDBFormatterTest extends \Monolog\Test\MonologTestCase
         $this->assertEquals(Level::Warning->value, $formattedRecord['level']);
         $this->assertEquals(Level::Warning->getName(), $formattedRecord['level_name']);
         $this->assertEquals('test', $formattedRecord['channel']);
-        $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $formattedRecord['datetime']);
+        $this->assertInstanceOf(UTCDateTime::class, $formattedRecord['datetime']);
         $this->assertEquals('1453410690123', $formattedRecord['datetime']->__toString());
         $this->assertEquals([], $formattedRecord['extra']);
     }
@@ -96,7 +91,7 @@ class MongoDBFormatterTest extends \Monolog\Test\MonologTestCase
         $formattedRecord = $formatter->format($record);
 
         $this->assertCount(5, $formattedRecord['context']);
-        $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $formattedRecord['context']['stuff']);
+        $this->assertInstanceOf(UTCDateTime::class, $formattedRecord['context']['stuff']);
         $this->assertEquals('-29731710213', $formattedRecord['context']['stuff']->__toString());
         $this->assertEquals(
             [

+ 14 - 15
tests/Monolog/Handler/MongoDBHandlerTest.php

@@ -11,11 +11,13 @@
 
 namespace Monolog\Handler;
 
+use MongoDB\BSON\UTCDateTime;
+use MongoDB\Client;
+use MongoDB\Collection;
 use MongoDB\Driver\Manager;
+use PHPUnit\Framework\Attributes\RequiresPhpExtension;
 
-/**
- * @requires extension mongodb
- */
+#[RequiresPhpExtension('mongodb')]
 class MongoDBHandlerTest extends \Monolog\Test\MonologTestCase
 {
     public function testConstructorShouldThrowExceptionForInvalidMongo()
@@ -27,42 +29,39 @@ class MongoDBHandlerTest extends \Monolog\Test\MonologTestCase
 
     public function testHandleWithLibraryClient()
     {
-        if (!(class_exists('MongoDB\Client'))) {
+        if (!class_exists(Client::class)) {
             $this->markTestSkipped('mongodb/mongodb not installed');
         }
 
-        $mongodb = $this->getMockBuilder('MongoDB\Client')
+        $client = $this->getMockBuilder(Client::class)
             ->disableOriginalConstructor()
             ->getMock();
 
-        $collection = $this->getMockBuilder('MongoDB\Collection')
+        $collection = $this->getMockBuilder(Collection::class)
             ->disableOriginalConstructor()
             ->getMock();
 
-        $mongodb->expects($this->once())
-            ->method('selectCollection')
+        $client->expects($this->once())
+            ->method('getCollection')
             ->with('db', 'collection')
             ->willReturn($collection);
 
         $record = $this->getRecord();
         $expected = $record->toArray();
-        $expected['datetime'] = new \MongoDB\BSON\UTCDateTime((int) floor(((float) $record->datetime->format('U.u')) * 1000));
+        $expected['datetime'] = new UTCDateTime((int) floor(((float) $record->datetime->format('U.u')) * 1000));
 
         $collection->expects($this->once())
             ->method('insertOne')
             ->with($expected);
 
-        $handler = new MongoDBHandler($mongodb, 'db', 'collection');
+        $handler = new MongoDBHandler($client, 'db', 'collection');
         $handler->handle($record);
     }
 
     public function testHandleWithDriverManager()
     {
-        /* This can become a unit test once ManagerInterface can be mocked.
-         * See: https://jira.mongodb.org/browse/PHPC-378
-         */
-        $mongodb = new Manager('mongodb://localhost:27017');
-        $handler = new MongoDBHandler($mongodb, 'test', 'monolog');
+        $manager = new Manager('mongodb://localhost:27017');
+        $handler = new MongoDBHandler($manager, 'test', 'monolog');
         $record = $this->getRecord();
 
         try {