diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml
new file mode 100644
index 00000000..1baaf6ca
--- /dev/null
+++ b/.github/workflows/coding-standards.yml
@@ -0,0 +1,66 @@
+name: "Coding Standards"
+
+on:
+ pull_request:
+ branches:
+ - "*.x"
+ push:
+ branches:
+ - "*.x"
+ schedule:
+ # Run workflow on every Sunday
+ - cron: '25 5 * * 0'
+
+jobs:
+ coding-standards:
+ name: "Coding Standards"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.2"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+
+ - name: Setup cache environment
+ id: extcache
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: ${{ matrix.php-version }}
+ extensions: "mongodb"
+ key: "extcache-v1"
+
+ - name: Cache extensions
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.extcache.outputs.dir }}
+ key: ${{ steps.extcache.outputs.key }}
+ restore-keys: ${{ steps.extcache.outputs.key }}
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ extensions: "mongodb"
+ php-version: "${{ matrix.php-version }}"
+ tools: "cs2pr"
+
+ - name: "Show driver information"
+ run: "php --ri mongodb"
+
+ - name: "Cache dependencies installed with Composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.json') }}"
+ restore-keys: "php-${{ matrix.php-version }}-composer-normal-"
+
+ - name: "Install dependencies with Composer"
+ run: "composer install --no-interaction --no-progress --no-suggest"
+
+ # The -q option is required until phpcs v4 is released
+ - name: "Run PHP_CodeSniffer"
+ run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..cc9ef9f3
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,171 @@
+name: "Tests"
+
+on:
+ pull_request:
+ branches:
+ - "*.x"
+ push:
+ branches:
+ - "*.x"
+ schedule:
+ # Run workflow on every Sunday
+ - cron: '25 5 * * 0'
+
+jobs:
+ verification:
+ name: "Verification tests"
+ runs-on: "ubuntu-20.04"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+ with:
+ fetch-depth: 2
+
+ - name: Setup cache environment
+ id: extcache
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: "5.6"
+ extensions: "mongodb-1.7.5, mongo-1.6.14"
+ key: "extcache-v1"
+
+ - name: Cache extensions
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.extcache.outputs.dir }}
+ key: ${{ steps.extcache.outputs.key }}
+ restore-keys: ${{ steps.extcache.outputs.key }}
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ php-version: "5.6"
+ tools: "pecl"
+ extensions: "mongodb-1.7.5, mongo-1.6.14"
+ coverage: "none"
+ ini-values: "zend.assertions=1"
+
+ - name: "Show legacy driver information"
+ run: "php --ri mongo"
+
+ - name: "Show driver information"
+ run: "php --ri mongodb"
+
+ - name: "Cache dependencies installed with composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-5.6-composer-locked-${{ hashFiles('composer.json') }}"
+ restore-keys: "php-5.6-composer-normal-"
+
+ - name: "Install dependencies with composer"
+ run: "composer update --no-interaction --no-progress"
+
+ - id: setup-mongodb
+ uses: mongodb-labs/drivers-evergreen-tools@master
+ with:
+ version: "3.0"
+
+ - name: "Run PHPUnit"
+ run: "vendor/bin/simple-phpunit -v"
+ env:
+ SYMFONY_DEPRECATIONS_HELPER: 999999
+ MONGODB_URI: ${{ steps.setup-mongodb.outputs.cluster-uri }}
+
+ phpunit:
+ name: "PHPUnit tests"
+ runs-on: "${{ matrix.os }}"
+
+ strategy:
+ fail-fast: true
+ matrix:
+ os:
+ - "ubuntu-20.04"
+ php-version:
+ - "7.2"
+ - "7.3"
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ mongodb-version:
+ - "4.4"
+ driver-version:
+ - "stable"
+ deps:
+ - "normal"
+ include:
+ - deps: "low"
+ os: "ubuntu-20.04"
+ php-version: "5.6"
+ mongodb-version: "3.0"
+ driver-version: "1.2.0"
+ - deps: "normal"
+ os: "ubuntu-20.04"
+ php-version: "7.0"
+ mongodb-version: "4.4"
+ driver-version: "1.9.2"
+ - deps: "normal"
+ os: "ubuntu-20.04"
+ php-version: "7.1"
+ mongodb-version: "4.4"
+ driver-version: "1.11.1"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+ with:
+ fetch-depth: 2
+
+ - name: Setup cache environment
+ id: extcache
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: ${{ matrix.php-version }}
+ extensions: "mongodb-${{ matrix.driver-version }}"
+ key: "extcache-v1"
+
+ - name: Cache extensions
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.extcache.outputs.dir }}
+ key: ${{ steps.extcache.outputs.key }}
+ restore-keys: ${{ steps.extcache.outputs.key }}
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ php-version: "${{ matrix.php-version }}"
+ tools: "pecl"
+ extensions: "mongodb-${{ matrix.driver-version }}"
+ coverage: "none"
+ ini-values: "zend.assertions=1"
+
+ - name: "Show driver information"
+ run: "php --ri mongodb"
+
+ - name: "Cache dependencies installed with composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.json') }}"
+ restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.deps }}-"
+
+ - name: "Install dependencies with composer"
+ run: "composer update --no-interaction --no-progress"
+ if: "${{ matrix.deps == 'normal' }}"
+
+ - name: "Install lowest possible dependencies with composer"
+ run: "composer update --no-interaction --no-progress --prefer-dist --prefer-lowest"
+ if: "${{ matrix.deps == 'low' }}"
+
+ - id: setup-mongodb
+ uses: mongodb-labs/drivers-evergreen-tools@master
+ with:
+ version: ${{ matrix.mongodb-version }}
+
+ - name: "Run PHPUnit"
+ run: "vendor/bin/simple-phpunit -v"
+ env:
+ SYMFONY_DEPRECATIONS_HELPER: 999999
+ MONGODB_URI: ${{ steps.setup-mongodb.outputs.cluster-uri }}
diff --git a/.gitignore b/.gitignore
index 18389124..772a9a08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
composer.lock
+.phpcs-cache
+.phpunit.result.cache
vendor/
tests/scripts/
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index 47a7f6dd..00000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-imports:
- - php
-
-tools:
- external_code_coverage:
- timeout: 1200 # Timeout in seconds.
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5123ec71..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-dist: trusty
-sudo: false
-language: php
-
-services:
- - mongodb
-
-php:
- - 7.0
- - 7.1
- - 7.2
-
-env:
- global:
- - DRIVER_VERSION="stable"
-
-addons:
- apt:
- sources:
- - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse"
- key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc"
- - "mongodb-upstart"
- packages: ['mongodb-org-server']
-
-matrix:
- fast_finish: true
- include:
- # Test against legacy driver to ensure validity of the test suite
- - php: 5.6
- env: DRIVER_VERSION=stable LEGACY_DRIVER_VERSION=stable
- # Test against set of lowest dependencies
- - php: 5.6
- env: DRIVER_VERSION="1.2.0" COMPOSER_FLAGS="--prefer-dist --prefer-lowest"
- addons:
- apt:
- sources:
- - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.0 multiverse"
- key_url: "https://www.mongodb.org/static/pgp/server-3.0.asc"
- - "mongodb-upstart"
- packages: ['mongodb-org-server']
-
-before_install:
- - pecl install -f mongodb-${DRIVER_VERSION}
- - composer update ${COMPOSER_FLAGS}
- - if [ "x$LEGACY_DRIVER_VERSION" != "x" ]; then yes '' | pecl -q install -f mongo-${LEGACY_DRIVER_VERSION}; fi
-
-script:
- - ./vendor/bin/phpunit --coverage-clover=coverage.clover
-
-after_script:
- - wget https://scrutinizer-ci.com/ocular.phar
- - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/CHANGELOG-1.1.md b/CHANGELOG-1.1.md
index 1aeae60f..f4b9e064 100644
--- a/CHANGELOG-1.1.md
+++ b/CHANGELOG-1.1.md
@@ -3,6 +3,98 @@ CHANGELOG for 1.1.x
This changelog references the relevant changes done in minor version updates.
+1.1.11 (2019-11-11)
+-------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.11](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.11)
+milestone.
+
+ * [#263](https://github.com/alcaeus/mongo-php-adapter/pull/263) fixes test
+ failures on PHP 7.4.
+ * [#262](https://github.com/alcaeus/mongo-php-adapter/pull/262) fixes a memory
+ leak due to generators that aren't freed.
+
+1.1.10 (2019-11-06)
+-------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.10](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.10)
+milestone.
+
+ * [#261](https://github.com/alcaeus/mongo-php-adapter/pull/261) fixes missing
+ interface implementations in cursor classes.
+ * [#260](https://github.com/alcaeus/mongo-php-adapter/pull/260) fixes issues
+ when running against MongoDB 4.2 or `ext-mongodb` 1.6.
+ * [#259](https://github.com/alcaeus/mongo-php-adapter/pull/259) fixes issues on
+ PHP 7.3 due to `MongoCursor` not implementing `Countable`.
+
+1.1.9 (2019-08-07)
+------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.9](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.9)
+milestone.
+
+ * [#255](https://github.com/alcaeus/mongo-php-adapter/pull/255) fixes inserting
+ documents with identifiers PHP considers empty.
+
+1.1.8 (2019-07-14)
+------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.8](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.8)
+milestone.
+
+ * [#253](https://github.com/alcaeus/mongo-php-adapter/pull/253) fixes wrong
+ handling of `ArrayObject` instances in `MongoCollection::insert`.
+
+1.1.7 (2019-04-06)
+------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.7](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.7)
+milestone.
+
+ * [#250](https://github.com/alcaeus/mongo-php-adapter/pull/250) fixes type
+ conversion when passing write concern to `MongoClient` via URL arguments.
+
+
+1.1.6 (2019-02-08)
+------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.6](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.6)
+milestone.
+
+ * [#244](https://github.com/alcaeus/mongo-php-adapter/pull/244) fixes a null
+ access when converting exceptions.
+ * [#236](https://github.com/alcaeus/mongo-php-adapter/pull/236) allows using
+ `0` as key in documents.
+ * [#234](https://github.com/alcaeus/mongo-php-adapter/pull/234) removes an
+ invalid attribute from phpunit.xml.
+
+
+1.1.5 (2018-03-05)
+-----------------
+
+All issues and pull requests under this release may be found under the
+[1.1.5](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.5)
+milestone.
+
+ * [#222](https://github.com/alcaeus/mongo-php-adapter/pull/222) fixes handling
+ of `monodb+srv` URLs in `MongoClient`.
+
+1.1.4 (2018-01-24)
+------------------
+
+All issues and pull requests under this release may be found under the
+[1.1.4](https://github.com/alcaeus/mongo-php-adapter/issues?q=milestone%3A1.1.4)
+milestone.
+
+ * [#214](https://github.com/alcaeus/mongo-php-adapter/pull/214) fixes the
+return values of MongoBatch calls with unacknowledged write concerns.
+
1.1.3 (2017-09-24)
------------------
diff --git a/README.md b/README.md
index 01df1f41..1cab6b7a 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ compatible with PHP 7.
This library aims to provide a compatibility layer for applications that rely on
libraries using ext-mongo, e.g.
[Doctrine MongoDB ODM](https://github.com/doctrine/mongodb-odm), but want to
-migrate to PHP 7 or HHVM on which `ext-mongo` will not run.
+migrate to PHP 7 on which `ext-mongo` will not run.
You should not be using this library if you do not rely on a library using
`ext-mongo`. If you are starting a new project, please check out
diff --git a/composer.json b/composer.json
index aa2ce17a..dcc3041a 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "alcaeus/mongo-php-adapter",
"type": "library",
- "description": "Adapter to provide ext-mongo interface on top of mongo-php-libary",
+ "description": "Adapter to provide ext-mongo interface on top of mongo-php-library",
"keywords": ["mongodb", "database"],
"license": "MIT",
"authors": [
@@ -9,13 +9,15 @@
{ "name": "Olivier Lechevalier", "email": "olivier.lechevalier@gmail.com" }
],
"require": {
- "php": "^5.6 || ^7.0",
+ "php": "^5.6 || ^7.0 || ^8.0",
+ "ext-ctype": "*",
"ext-hash": "*",
"ext-mongodb": "^1.2.0",
"mongodb/mongodb": "^1.0.1"
},
"require-dev": {
- "phpunit/phpunit": "^5.7 || ^6.0"
+ "symfony/phpunit-bridge": "^4.4.16 || ^5.2",
+ "squizlabs/php_codesniffer": "^3.2"
},
"provide": {
"ext-mongo": "1.6.14"
@@ -33,8 +35,6 @@
"psr-4": { "Alcaeus\\MongoDbAdapter\\Tests\\": "tests/Alcaeus/MongoDbAdapter" }
},
"extra": {
- "branch-alias": {
- "dev-master": "1.1.x-dev"
- }
+ "branch-version": "1.x"
}
}
diff --git a/lib/Alcaeus/MongoDbAdapter/AbstractCursor.php b/lib/Alcaeus/MongoDbAdapter/AbstractCursor.php
index 6e3c8758..f1d76ebb 100644
--- a/lib/Alcaeus/MongoDbAdapter/AbstractCursor.php
+++ b/lib/Alcaeus/MongoDbAdapter/AbstractCursor.php
@@ -18,6 +18,7 @@
use Alcaeus\MongoDbAdapter\Helper\ReadPreference;
use MongoDB\Collection;
use MongoDB\Driver\Cursor;
+use ReturnTypeWillChange;
/**
* @internal
@@ -52,7 +53,7 @@ abstract class AbstractCursor
protected $db;
/**
- * @var \Iterator
+ * @var CursorIterator
*/
protected $iterator;
@@ -136,6 +137,7 @@ public function __construct(\MongoClient $connection, $ns)
* @link http://www.php.net/manual/en/mongocursor.current.php
* @return array
*/
+ #[ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -146,6 +148,7 @@ public function current()
* @link http://www.php.net/manual/en/mongocursor.key.php
* @return string The current result's _id as a string.
*/
+ #[ReturnTypeWillChange]
public function key()
{
return $this->key;
@@ -158,6 +161,7 @@ public function key()
* @throws \MongoCursorTimeoutException
* @return array Returns the next object
*/
+ #[ReturnTypeWillChange]
public function next()
{
if (! $this->startedIterating) {
@@ -181,6 +185,7 @@ public function next()
* @throws \MongoCursorTimeoutException
* @return void
*/
+ #[ReturnTypeWillChange]
public function rewind()
{
// We can recreate the cursor to allow it to be rewound
@@ -196,6 +201,7 @@ public function rewind()
* @link http://www.php.net/manual/en/mongocursor.valid.php
* @return boolean If the current result is not null.
*/
+ #[ReturnTypeWillChange]
public function valid()
{
return $this->valid;
@@ -292,9 +298,8 @@ protected function getOptions($optionNames = null)
protected function ensureIterator()
{
if ($this->iterator === null) {
- // MongoDB\Driver\Cursor needs to be wrapped into a \Generator so that a valid \Iterator with working implementations of
- // next, current, valid, key and rewind is returned. These methods don't work if we wrap the Cursor inside an \IteratorIterator
$this->iterator = $this->wrapTraversable($this->ensureCursor());
+ $this->iterator->rewind();
}
return $this->iterator;
@@ -302,13 +307,11 @@ protected function ensureIterator()
/**
* @param \Traversable $traversable
- * @return \Generator
+ * @return CursorIterator
*/
protected function wrapTraversable(\Traversable $traversable)
{
- foreach ($traversable as $key => $value) {
- yield $key => $value;
- }
+ return new CursorIterator($traversable);
}
/**
diff --git a/lib/Alcaeus/MongoDbAdapter/CursorIterator.php b/lib/Alcaeus/MongoDbAdapter/CursorIterator.php
new file mode 100644
index 00000000..31057584
--- /dev/null
+++ b/lib/Alcaeus/MongoDbAdapter/CursorIterator.php
@@ -0,0 +1,40 @@
+useIdAsKey = $useIdAsKey;
+ }
+
+ #[ReturnTypeWillChange]
+ public function key()
+ {
+ if (!$this->useIdAsKey) {
+ return parent::key();
+ }
+
+ $current = $this->current();
+
+ if (!isset($current->_id) || (is_object($current->_id) && !$current->_id instanceof ObjectID)) {
+ return parent::key();
+ }
+
+ return (string) $current->_id;
+ }
+}
diff --git a/lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php b/lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php
index 8838a698..9bc442ae 100644
--- a/lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php
+++ b/lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php
@@ -16,6 +16,8 @@
namespace Alcaeus\MongoDbAdapter;
use MongoDB\Driver\Exception;
+use MongoDB\Driver\WriteError;
+use MongoDB\Driver\WriteResult;
/**
* @internal
@@ -30,6 +32,12 @@ class ExceptionConverter
*/
public static function toLegacy(Exception\Exception $e, $fallbackClass = 'MongoException')
{
+ // Starting with ext-mongodb 1.6.0, errors during bulk write are always wrapped in a BulkWriteException.
+ // If a BulkWriteException wraps another driver exception, use that instead.
+ if ($e instanceof Exception\BulkWriteException && $e->getPrevious() instanceof Exception\Exception) {
+ $e = $e->getPrevious();
+ }
+
$message = $e->getMessage();
$code = $e->getCode();
@@ -44,12 +52,13 @@ public static function toLegacy(Exception\Exception $e, $fallbackClass = 'MongoE
case Exception\BulkWriteException::class:
case Exception\WriteException::class:
$writeResult = $e->getWriteResult();
-
- if ($writeResult) {
+ // attempt to retrieve write error
+ if ($writeResult instanceof WriteResult && is_array($writeResult->getWriteErrors()) && $writeResult->getWriteErrors() !== []) {
$writeError = $writeResult->getWriteErrors()[0];
-
- $message = $writeError->getMessage();
- $code = $writeError->getCode();
+ if ($writeError instanceof WriteError) {
+ $message = $writeError->getMessage();
+ $code = $writeError->getCode();
+ }
}
switch ($code) {
diff --git a/lib/Alcaeus/MongoDbAdapter/TypeConverter.php b/lib/Alcaeus/MongoDbAdapter/TypeConverter.php
index ad10ba75..51ad7c6c 100644
--- a/lib/Alcaeus/MongoDbAdapter/TypeConverter.php
+++ b/lib/Alcaeus/MongoDbAdapter/TypeConverter.php
@@ -43,6 +43,8 @@ public static function fromLegacy($value)
return $value->toBSONType();
case $value instanceof BSON\Type:
return $value;
+ case $value instanceof \DateTimeInterface:
+ return self::fromLegacy((array) $value);
case is_array($value):
case is_object($value):
$result = [];
@@ -176,7 +178,7 @@ private static function convertBSONObjectToLegacy(BSON\Type $value)
case $value instanceof Model\BSONDocument:
case $value instanceof Model\BSONArray:
return array_map(
- ['self', 'toLegacy'],
+ [self::class, 'toLegacy'],
$value->getArrayCopy()
);
default:
diff --git a/lib/Alcaeus/MongoDbAdapter/TypeInterface.php b/lib/Alcaeus/MongoDbAdapter/TypeInterface.php
index 3f079830..2126e161 100644
--- a/lib/Alcaeus/MongoDbAdapter/TypeInterface.php
+++ b/lib/Alcaeus/MongoDbAdapter/TypeInterface.php
@@ -15,8 +15,6 @@
namespace Alcaeus\MongoDbAdapter;
-use MongoDB\BSON\Type;
-
interface TypeInterface
{
/**
diff --git a/lib/Mongo/MongoClient.php b/lib/Mongo/MongoClient.php
index 0e0fae57..a88f10e6 100644
--- a/lib/Mongo/MongoClient.php
+++ b/lib/Mongo/MongoClient.php
@@ -32,13 +32,13 @@ class MongoClient
use Helper\WriteConcern;
const VERSION = '1.6.12';
- const DEFAULT_HOST = "localhost" ;
- const DEFAULT_PORT = 27017 ;
- const RP_PRIMARY = "primary" ;
- const RP_PRIMARY_PREFERRED = "primaryPreferred" ;
- const RP_SECONDARY = "secondary" ;
- const RP_SECONDARY_PREFERRED = "secondaryPreferred" ;
- const RP_NEAREST = "nearest" ;
+ const DEFAULT_HOST = "localhost";
+ const DEFAULT_PORT = 27017;
+ const RP_PRIMARY = "primary";
+ const RP_PRIMARY_PREFERRED = "primaryPreferred";
+ const RP_SECONDARY = "secondary";
+ const RP_SECONDARY_PREFERRED = "secondaryPreferred";
+ const RP_NEAREST = "nearest";
/**
* @var bool
@@ -93,10 +93,10 @@ public function __construct($server = 'default', array $options = ['connect' =>
$this->applyConnectionOptions($server, $options);
$this->server = $server;
- if (false === strpos($this->server, 'mongodb://')) {
- $this->server = 'mongodb://'.$this->server;
+ if (false === strpos($this->server, '://')) {
+ $this->server = 'mongodb://' . $this->server;
}
- $this->client = new Client($this->server, $options, $driverOptions);
+ $this->client = new Client($this->server, $options, $driverOptions + ['driver' => ['name' => 'mongo-php-adapter']]);
$info = $this->client->__debugInfo();
$this->manager = $info['manager'];
@@ -222,7 +222,7 @@ public function getHosts()
$results[$key] = [
'host' => $server->getHost(),
'port' => $server->getPort(),
- 'health' => (int) $info['ok'],
+ 'health' => 1,
'state' => $state,
'ping' => $server->getLatency(),
'lastPing' => null,
@@ -352,7 +352,7 @@ private function notImplemented()
/**
* @return array
*/
- function __sleep()
+ public function __sleep()
{
return [
'connected', 'status', 'server', 'persistent'
@@ -365,7 +365,12 @@ function __sleep()
*/
private function extractUrlOptions($server)
{
- $queryOptions = explode('&', parse_url($server, PHP_URL_QUERY));
+ $queryOptions = parse_url($server, PHP_URL_QUERY);
+ if (!$queryOptions) {
+ return [];
+ }
+
+ $queryOptions = explode('&', $queryOptions);
$options = [];
foreach ($queryOptions as $option) {
@@ -376,6 +381,8 @@ private function extractUrlOptions($server)
$keyValue = explode('=', $option);
if ($keyValue[0] === 'readPreferenceTags') {
$options[$keyValue[0]][] = $this->getReadPreferenceTags($keyValue[1]);
+ } elseif (ctype_digit($keyValue[1])) {
+ $options[$keyValue[0]] = (int) $keyValue[1];
} else {
$options[$keyValue[0]] = $keyValue[1];
}
diff --git a/lib/Mongo/MongoCode.php b/lib/Mongo/MongoCode.php
index 25d6b5f0..dd792f80 100644
--- a/lib/Mongo/MongoCode.php
+++ b/lib/Mongo/MongoCode.php
@@ -27,7 +27,7 @@ class MongoCode implements \Alcaeus\MongoDbAdapter\TypeInterface
private $code;
/**
- * @var array
+ * @var array|null
*/
private $scope;
@@ -65,6 +65,6 @@ public function __toString()
*/
public function toBSONType()
{
- return new \MongoDB\BSON\Javascript($this->code, $this->scope);
+ return new \MongoDB\BSON\Javascript($this->code, !empty($this->scope) ? $this->scope : null);
}
}
diff --git a/lib/Mongo/MongoCollection.php b/lib/Mongo/MongoCollection.php
index 64070b70..aa858200 100644
--- a/lib/Mongo/MongoCollection.php
+++ b/lib/Mongo/MongoCollection.php
@@ -20,6 +20,8 @@
use Alcaeus\MongoDbAdapter\Helper;
use Alcaeus\MongoDbAdapter\TypeConverter;
use Alcaeus\MongoDbAdapter\ExceptionConverter;
+use MongoDB\Driver\Exception\CommandException;
+use MongoDB\Model\IndexInput;
/**
* Represents a database collection.
@@ -277,7 +279,7 @@ public function validate($scan_data = false)
*/
public function insert(&$a, array $options = [])
{
- if (! $this->ensureDocumentHasMongoId($a)) {
+ if ($this->ensureDocumentHasMongoId($a) === null) {
trigger_error(sprintf('%s(): expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_USER_WARNING);
return;
}
@@ -596,7 +598,7 @@ public function createIndex($keys, array $options = [])
}
if (! isset($options['name'])) {
- $options['name'] = \MongoDB\generate_index_name($keys);
+ $options['name'] = $this->generateIndexName($keys);
}
$indexes = iterator_to_array($this->collection->listIndexes());
@@ -626,7 +628,9 @@ public function createIndex($keys, array $options = [])
$this->collection->createIndex($keys, $options);
} catch (\MongoDB\Driver\Exception\Exception $e) {
- throw ExceptionConverter::toLegacy($e, 'MongoResultException');
+ if (! $e instanceof CommandException || strpos($e->getMessage(), 'with a different name') === false) {
+ throw ExceptionConverter::toLegacy($e, 'MongoResultException');
+ }
}
$result = [
@@ -674,7 +678,7 @@ public function deleteIndex($keys)
$indexName .= '_1';
}
} elseif (is_array($keys)) {
- $indexName = \MongoDB\generate_index_name($keys);
+ $indexName = $this->generateIndexName($keys);
} else {
throw new \InvalidArgumentException();
}
@@ -888,14 +892,14 @@ public function group($keys, array $initial, $reduce, array $condition = [])
$command = [
'group' => [
'ns' => $this->name,
- '$reduce' => (string)$reduce,
+ '$reduce' => (string) $reduce,
'initial' => $initial,
'cond' => $condition,
],
];
if ($keys instanceof MongoCode) {
- $command['group']['$keyf'] = (string)$keys;
+ $command['group']['$keyf'] = (string) $keys;
} else {
$command['group']['key'] = $keys;
}
@@ -904,7 +908,7 @@ public function group($keys, array $initial, $reduce, array $condition = [])
}
if (array_key_exists('finalize', $condition)) {
if ($condition['finalize'] instanceof MongoCode) {
- $condition['finalize'] = (string)$condition['finalize'];
+ $condition['finalize'] = (string) $condition['finalize'];
}
$command['group']['finalize'] = $condition['finalize'];
}
@@ -983,7 +987,7 @@ private function convertWriteConcernOptions(array $options)
private function checkKeys(array $array)
{
foreach ($array as $key => $value) {
- if (empty($key) && $key !== 0) {
+ if (empty($key) && $key !== 0 && $key !== '0') {
throw new \MongoException('zero-length keys are not allowed, did you use $ with double quotes?');
}
@@ -999,12 +1003,12 @@ private function checkKeys(array $array)
*/
private function ensureDocumentHasMongoId(&$document)
{
- if (is_array($document)) {
+ if (is_array($document) || $document instanceof ArrayObject) {
if (! isset($document['_id'])) {
$document['_id'] = new \MongoId();
}
- $this->checkKeys($document);
+ $this->checkKeys((array) $document);
return $document['_id'];
} elseif (is_object($document)) {
@@ -1036,6 +1040,22 @@ private function checkCollectionName($name)
}
}
+
+ /**
+ * @param array $keys Field or fields to use as index.
+ * @return string
+ */
+ private function generateIndexName($keys)
+ {
+ $name = '';
+
+ foreach ($keys as $field => $type) {
+ $name .= ($name !== '' ? '_' : '') . $field . '_' . $type;
+ }
+
+ return $name;
+ }
+
/**
* @return array
*/
diff --git a/lib/Mongo/MongoCursor.php b/lib/Mongo/MongoCursor.php
index e98a2671..07be69aa 100644
--- a/lib/Mongo/MongoCursor.php
+++ b/lib/Mongo/MongoCursor.php
@@ -18,17 +18,17 @@
}
use Alcaeus\MongoDbAdapter\AbstractCursor;
+use Alcaeus\MongoDbAdapter\CursorIterator;
use Alcaeus\MongoDbAdapter\TypeConverter;
use Alcaeus\MongoDbAdapter\ExceptionConverter;
use MongoDB\Driver\Cursor;
-use MongoDB\Driver\ReadPreference;
use MongoDB\Operation\Find;
/**
* Result object for database query.
* @link http://www.php.net/manual/en/class.mongocursor.php
*/
-class MongoCursor extends AbstractCursor implements Iterator
+class MongoCursor extends AbstractCursor implements Iterator, Countable, MongoCursorInterface
{
/**
* @var bool
@@ -133,6 +133,7 @@ public function awaitData($wait = true)
* @param bool $foundOnly Send cursor limit and skip information to the count function, if applicable.
* @return int The number of documents returned by this cursor's query.
*/
+ #[\ReturnTypeWillChange]
public function count($foundOnly = false)
{
$optionNames = ['hint', 'maxTimeMS'];
@@ -472,16 +473,11 @@ protected function ensureCursor()
/**
* @param \Traversable $traversable
- * @return \Generator
+ * @return CursorIterator
*/
protected function wrapTraversable(\Traversable $traversable)
{
- foreach ($traversable as $key => $value) {
- if (isset($value->_id) && ($value->_id instanceof \MongoDB\BSON\ObjectID || !is_object($value->_id))) {
- $key = (string) $value->_id;
- }
- yield $key => $value;
- }
+ return new CursorIterator($traversable, true);
}
/**
diff --git a/lib/Mongo/MongoDB.php b/lib/Mongo/MongoDB.php
index 61750db3..f9a0f112 100644
--- a/lib/Mongo/MongoDB.php
+++ b/lib/Mongo/MongoDB.php
@@ -154,7 +154,9 @@ public function getCollectionInfo(array $options = [])
'info' => isset($info['info']) ? (array) $info['info'] : null,
'idIndex' => isset($info['idIndex']) ? (array) $info['idIndex'] : null,
],
- function ($item) { return $item !== null; }
+ function ($item) {
+ return $item !== null;
+ }
);
};
diff --git a/lib/Mongo/MongoDate.php b/lib/Mongo/MongoDate.php
index 6c1e657a..7571ca84 100644
--- a/lib/Mongo/MongoDate.php
+++ b/lib/Mongo/MongoDate.php
@@ -88,15 +88,14 @@ public function toBSONType()
public function toDateTime()
{
$datetime = new \DateTime();
+ $datetime->setTimezone(new \DateTimeZone("UTC"));
$datetime->setTimestamp($this->sec);
$microSeconds = $this->truncateMicroSeconds($this->usec);
if ($microSeconds > 0) {
- $datetime = \DateTime::createFromFormat('Y-m-d H:i:s.u', $datetime->format('Y-m-d H:i:s') . '.' . str_pad($microSeconds, 6, '0', STR_PAD_LEFT));
+ $datetime = \DateTime::createFromFormat('Y-m-d H:i:s.u e', $datetime->format('Y-m-d H:i:s') . '.' . str_pad($microSeconds, 6, '0', STR_PAD_LEFT) . ' UTC');
}
- $datetime->setTimezone(new \DateTimeZone("UTC"));
-
return $datetime;
}
diff --git a/lib/Mongo/MongoGridFS.php b/lib/Mongo/MongoGridFS.php
index 56718d3d..7105fbaa 100644
--- a/lib/Mongo/MongoGridFS.php
+++ b/lib/Mongo/MongoGridFS.php
@@ -207,7 +207,7 @@ public function storeBytes($bytes, array $extra = [], array $options = [])
try {
$file = $this->insertFile($record, $options);
} catch (MongoException $e) {
- throw new MongoGridFSException('Could not store file: '. $e->getMessage(), $e->getCode(), $e);
+ throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), $e->getCode(), $e);
}
try {
diff --git a/lib/Mongo/MongoGridFSCursor.php b/lib/Mongo/MongoGridFSCursor.php
index bec7b444..3da1542a 100644
--- a/lib/Mongo/MongoGridFSCursor.php
+++ b/lib/Mongo/MongoGridFSCursor.php
@@ -17,7 +17,7 @@
return;
}
-class MongoGridFSCursor extends MongoCursor
+class MongoGridFSCursor extends MongoCursor implements Countable
{
/**
* @static
@@ -68,6 +68,6 @@ public function current()
public function key()
{
$file = $this->current();
- return ($file !== null) ? (string)$file->file['_id'] : null;
+ return ($file !== null) ? (string) $file->file['_id'] : null;
}
}
diff --git a/lib/Mongo/MongoId.php b/lib/Mongo/MongoId.php
index a83b98eb..7a665b41 100644
--- a/lib/Mongo/MongoId.php
+++ b/lib/Mongo/MongoId.php
@@ -202,6 +202,7 @@ public static function __set_state(array $props)
/**
* @return stdClass
*/
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
$object = new stdClass();
diff --git a/lib/Mongo/MongoWriteBatch.php b/lib/Mongo/MongoWriteBatch.php
index 399cc965..991f2450 100644
--- a/lib/Mongo/MongoWriteBatch.php
+++ b/lib/Mongo/MongoWriteBatch.php
@@ -83,7 +83,7 @@ protected function __construct(MongoCollection $collection, $batchType, $writeOp
public function add($item)
{
if (is_object($item)) {
- $item = (array)$item;
+ $item = (array) $item;
}
$this->validate($item);
@@ -132,6 +132,17 @@ final public function execute(array $writeOptions = [])
switch ($this->batchType) {
case self::COMMAND_UPDATE:
+ if ($options['writeConcern']->getW() === 0) {
+ $resultDocument += [
+ 'nMatched' => 0,
+ 'nModified' => 0,
+ 'nUpserted' => 0,
+ 'ok' => true,
+ ];
+
+ break;
+ }
+
$upsertedIds = [];
foreach ($writeResult->getUpsertedIds() as $index => $id) {
$upsertedIds[] = [
@@ -153,6 +164,15 @@ final public function execute(array $writeOptions = [])
break;
case self::COMMAND_DELETE:
+ if ($options['writeConcern']->getW() === 0) {
+ $resultDocument += [
+ 'nRemoved' => 0,
+ 'ok' => true,
+ ];
+
+ break;
+ }
+
$resultDocument += [
'nRemoved' => $writeResult->getDeletedCount(),
'ok' => true,
@@ -160,6 +180,15 @@ final public function execute(array $writeOptions = [])
break;
case self::COMMAND_INSERT:
+ if ($options['writeConcern']->getW() === 0) {
+ $resultDocument += [
+ 'nInserted' => 0,
+ 'ok' => true,
+ ];
+
+ break;
+ }
+
$resultDocument += [
'nInserted' => $writeResult->getInsertedCount(),
'ok' => true,
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 00000000..ca441e66
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+ lib
+ tests
+
+
+ */tests/*
+
+
+
+
+
+
+
+
+
+
+ */lib/Mongo/*
+
+
+
+
+ error
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 3a237d32..eaa58e59 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -7,12 +7,13 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false"
- syntaxCheck="false"
+ bootstrap="vendor/autoload.php"
>
+
@@ -20,10 +21,4 @@
./tests/Alcaeus/MongoDbAdapter/
-
-
-
- ./lib/Alcaeus/MongoDbAdapter
-
-
diff --git a/tests/Alcaeus/MongoDbAdapter/Constraint/Constraint.php b/tests/Alcaeus/MongoDbAdapter/Constraint/Constraint.php
new file mode 100644
index 00000000..8a908f1c
--- /dev/null
+++ b/tests/Alcaeus/MongoDbAdapter/Constraint/Constraint.php
@@ -0,0 +1,16 @@
+value = self::prepare($value);
+ $this->allowExtraRootKeys = $allowExtraRootKeys;
+ $this->allowExtraKeys = $allowExtraKeys;
+ $this->allowOperators = $allowOperators;
+ $this->comparatorFactory = Factory::getInstance();
+ }
+
+ private function doEvaluate($other, $description = '', $returnResult = false)
+ {
+ $other = self::prepare($other);
+ $success = false;
+ $this->lastFailure = null;
+
+ try {
+ $this->assertMatches($this->value, $other);
+ $success = true;
+ } catch (ExpectationFailedException $e) {
+ /* Rethrow internal assertion failures (e.g. operator type checks,
+ * EntityMap errors), which are logical errors in the code/test. */
+ throw $e;
+ } catch (RuntimeException $e) {
+ /* This will generally catch internal errors from failAt(), which
+ * include a key path to pinpoint the failure. */
+ $this->lastFailure = new ComparisonFailure(
+ $this->value,
+ $other,
+ /* TODO: Improve the exporter to canonicalize documents by
+ * sorting keys and remove spl_object_hash from output. */
+ $this->exporter()->export($this->value),
+ $this->exporter()->export($other),
+ false,
+ $e->getMessage()
+ );
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (! $success) {
+ $this->fail($other, $description, $this->lastFailure);
+ }
+ }
+
+ private function assertEquals($expected, $actual, $keyPath)
+ {
+ $expectedType = is_object($expected) ? get_class($expected) : gettype($expected);
+ $actualType = is_object($actual) ? get_class($actual) : gettype($actual);
+
+ /* Early check to work around ObjectComparator printing the entire value
+ * for a failed type comparison. Avoid doing this if either value is
+ * numeric to allow for flexible numeric comparisons (e.g. 1 == 1.0). */
+ if ($expectedType !== $actualType && ! (self::isNumeric($expected) || self::isNumeric($actual))) {
+ self::failAt(sprintf('%s is not expected type "%s"', $actualType, $expectedType), $keyPath);
+ }
+
+ try {
+ $this->comparatorFactory->getComparatorFor($expected, $actual)->assertEquals($expected, $actual);
+ } catch (ComparisonFailure $e) {
+ /* Disregard other ComparisonFailure fields, as evaluate() only uses
+ * the message when creating its own ComparisonFailure. */
+ self::failAt($e->getMessage(), $keyPath);
+ }
+ }
+
+ private function assertMatches($expected, $actual, $keyPath = '')
+ {
+ if ($expected instanceof BSONArray) {
+ $this->assertMatchesArray($expected, $actual, $keyPath);
+
+ return;
+ }
+
+ if ($expected instanceof BSONDocument) {
+ $this->assertMatchesDocument($expected, $actual, $keyPath);
+
+ return;
+ }
+
+ $this->assertEquals($expected, $actual, $keyPath);
+ }
+
+ private function assertMatchesArray(BSONArray $expected, $actual, $keyPath)
+ {
+ if (! $actual instanceof BSONArray) {
+ $actualType = is_object($actual) ? get_class($actual) : gettype($actual);
+ self::failAt(sprintf('%s is not instance of expected class "%s"', $actualType, BSONArray::class), $keyPath);
+ }
+
+ if (count($expected) !== count($actual)) {
+ self::failAt(sprintf('$actual count is %d, expected %d', count($actual), count($expected)), $keyPath);
+ }
+
+ foreach ($expected as $key => $expectedValue) {
+ $this->assertMatches(
+ $expectedValue,
+ $actual[$key],
+ (empty($keyPath) ? $key : $keyPath . '.' . $key)
+ );
+ }
+ }
+
+ private function assertMatchesDocument(BSONDocument $expected, $actual, $keyPath)
+ {
+ if ($this->allowOperators && self::isOperator($expected)) {
+ $this->assertMatchesOperator($expected, $actual, $keyPath);
+
+ return;
+ }
+
+ if (! $actual instanceof BSONDocument) {
+ $actualType = is_object($actual) ? get_class($actual) : gettype($actual);
+ self::failAt(sprintf('%s is not instance of expected class "%s"', $actualType, BSONDocument::class), $keyPath);
+ }
+
+ foreach ($expected as $key => $expectedValue) {
+ $actualKeyExists = $actual->offsetExists($key);
+
+ if ($this->allowOperators && $expectedValue instanceof BSONDocument && self::isOperator($expectedValue)) {
+ $operatorName = self::getOperatorName($expectedValue);
+
+ if ($operatorName === '$$exists') {
+ Assert::assertIsBool($expectedValue['$$exists'], '$$exists requires bool');
+
+ if ($expectedValue['$$exists'] && ! $actualKeyExists) {
+ self::failAt(sprintf('$actual does not have expected key "%s"', $key), $keyPath);
+ }
+
+ if (! $expectedValue['$$exists'] && $actualKeyExists) {
+ self::failAt(sprintf('$actual has unexpected key "%s"', $key), $keyPath);
+ }
+
+ continue;
+ }
+
+ if ($operatorName === '$$unsetOrMatches') {
+ if (! $actualKeyExists) {
+ continue;
+ }
+
+ $expectedValue = $expectedValue['$$unsetOrMatches'];
+ }
+ }
+
+ if (! $actualKeyExists) {
+ self::failAt(sprintf('$actual does not have expected key "%s"', $key), $keyPath);
+ }
+
+ $this->assertMatches(
+ $expectedValue,
+ $actual[$key],
+ (empty($keyPath) ? $key : $keyPath . '.' . $key)
+ );
+ }
+
+ // Ignore extra keys in root documents
+ if ($this->allowExtraKeys || ($this->allowExtraRootKeys && empty($keyPath))) {
+ return;
+ }
+
+ foreach ($actual as $key => $_) {
+ if (! $expected->offsetExists($key)) {
+ self::failAt(sprintf('$actual has unexpected key "%s"', $key), $keyPath);
+ }
+ }
+ }
+
+ private function assertMatchesOperator(BSONDocument $operator, $actual, $keyPath)
+ {
+ $name = self::getOperatorName($operator);
+
+ if ($name === '$$unsetOrMatches') {
+ /* If the operator is used at the top level, consider null values
+ * for $actual to be unset. If the operator is nested, this check is
+ * done later during document iteration. */
+ if ($keyPath === '' && $actual === null) {
+ return;
+ }
+
+ $this->assertMatches(
+ self::prepare($operator['$$unsetOrMatches']),
+ $actual,
+ $keyPath
+ );
+
+ return;
+ }
+
+ throw new LogicException('unsupported operator: ' . $name);
+ }
+
+ /** @see ConstraintTrait */
+ private function doAdditionalFailureDescription($other)
+ {
+ if ($this->lastFailure === null) {
+ return '';
+ }
+
+ return $this->lastFailure->getMessage();
+ }
+
+ /** @see ConstraintTrait */
+ private function doFailureDescription($other)
+ {
+ return 'expected value matches actual value';
+ }
+
+ /** @see ConstraintTrait */
+ private function doMatches($other)
+ {
+ $other = self::prepare($other);
+
+ try {
+ $this->assertMatches($this->value, $other);
+ } catch (RuntimeException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** @see ConstraintTrait */
+ private function doToString()
+ {
+ return 'matches ' . $this->exporter()->export($this->value);
+ }
+
+ private static function failAt(string $message, string $keyPath)
+ {
+ $prefix = empty($keyPath) ? '' : sprintf('Field path "%s": ', $keyPath);
+
+ throw new RuntimeException($prefix . $message);
+ }
+
+ private static function getOperatorName(BSONDocument $document)
+ {
+ foreach ($document as $key => $_) {
+ if (strpos((string) $key, '$$') === 0) {
+ return $key;
+ }
+ }
+
+ throw new LogicException('should not reach this point');
+ }
+
+ private static function isNumeric($value)
+ {
+ return is_int($value) || is_float($value) || $value instanceof Int64;
+ }
+
+ private static function isOperator(BSONDocument $document)
+ {
+ if (count($document) !== 1) {
+ return false;
+ }
+
+ foreach ($document as $key => $_) {
+ return strpos((string) $key, '$$') === 0;
+ }
+
+ throw new LogicException('should not reach this point');
+ }
+
+ /**
+ * Prepare a value for comparison.
+ *
+ * If the value is an array or object, it will be converted to a BSONArray
+ * or BSONDocument. If $value is an array and $isRoot is true, it will be
+ * converted to a BSONDocument; otherwise, it will be converted to a
+ * BSONArray or BSONDocument based on its keys. Each value within an array
+ * or document will then be prepared recursively.
+ *
+ * @param mixed $bson
+ * @return mixed
+ */
+ private static function prepare($bson)
+ {
+ if (! is_array($bson) && ! is_object($bson)) {
+ return $bson;
+ }
+
+ /* Convert Int64 objects to integers on 64-bit platforms for
+ * compatibility reasons. */
+ if ($bson instanceof Int64 && PHP_INT_SIZE != 4) {
+ return (int) ((string) $bson);
+ }
+
+ /* TODO: Convert Int64 objects to integers on 32-bit platforms if they
+ * can be expressed as such. This is necessary to handle flexible
+ * numeric comparisons if the server returns 32-bit value as a 64-bit
+ * integer (e.g. cursor ID). */
+
+ // Serializable can produce an array or object, so recurse on its output
+ if ($bson instanceof Serializable) {
+ return self::prepare($bson->bsonSerialize());
+ }
+
+ /* Serializable has already been handled, so any remaining instances of
+ * Type will not serialize as BSON arrays or objects */
+ if ($bson instanceof Type) {
+ return $bson;
+ }
+
+ if (is_array($bson) && self::isArrayEmptyOrIndexed($bson)) {
+ $bson = new BSONArray($bson);
+ }
+
+ if (! $bson instanceof BSONArray && ! $bson instanceof BSONDocument) {
+ /* If $bson is an object, any numeric keys may become inaccessible.
+ * We can work around this by casting back to an array. */
+ $bson = new BSONDocument((array) $bson);
+ }
+
+ foreach ($bson as $key => $value) {
+ if (is_array($value) || is_object($value)) {
+ $bson[$key] = self::prepare($value);
+ }
+ }
+
+ return $bson;
+ }
+
+ private static function isArrayEmptyOrIndexed(array $a)
+ {
+ if (empty($a)) {
+ return true;
+ }
+
+ return array_keys($a) === range(0, count($a) - 1);
+ }
+}
diff --git a/tests/Alcaeus/MongoDbAdapter/Constraint/README.md b/tests/Alcaeus/MongoDbAdapter/Constraint/README.md
new file mode 100644
index 00000000..36745fe8
--- /dev/null
+++ b/tests/Alcaeus/MongoDbAdapter/Constraint/README.md
@@ -0,0 +1,6 @@
+Document Constraints for PHPUnit
+================================
+
+The constraints in this directory have been copied from the MongoDB Library at
+https://github.com/mongodb/mongo-php-library. This code is licensed under the
+Apache License Version 2.0.
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoBinDataTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoBinDataTest.php
index c6fbac9e..5b2abb1b 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoBinDataTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoBinDataTest.php
@@ -15,10 +15,10 @@ class MongoBinDataTest extends TestCase
public function testCreate()
{
$bin = new \MongoBinData(self::GUID, \MongoBinData::FUNC);
- $this->assertAttributeSame(self::GUID, 'bin', $bin);
- $this->assertAttributeSame(\MongoBinData::FUNC, 'type', $bin);
+ $this->assertSame(self::GUID, $bin->bin);
+ $this->assertSame(\MongoBinData::FUNC, $bin->type);
- $this->assertSame('', (string)$bin);
+ $this->assertSame('', (string) $bin);
return $bin;
}
@@ -44,7 +44,7 @@ public function testCreateWithBsonBinary()
$bsonBinary = new \MongoDB\BSON\Binary(self::GUID, \MongoDB\BSON\Binary::TYPE_UUID);
$bin = new \MongoBinData($bsonBinary);
- $this->assertAttributeSame(self::GUID, 'bin', $bin);
- $this->assertAttributeSame(\MongoBinData::UUID_RFC4122, 'type', $bin);
+ $this->assertSame(self::GUID, $bin->bin);
+ $this->assertSame(\MongoBinData::UUID_RFC4122, $bin->type);
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoClientTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoClientTest.php
index d0edc4dd..de13f786 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoClientTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoClientTest.php
@@ -9,9 +9,25 @@
*/
class MongoClientTest extends TestCase
{
+ /**
+ * @dataProvider provideConnectionUri
+ */
+ public function testConnectionUri($uri, $expected)
+ {
+ $this->skipTestIf(extension_loaded('mongo'));
+ $this->assertSame($expected, (string) (new \MongoClient($uri, ['connect' => false])));
+ }
+
+ public function provideConnectionUri()
+ {
+ yield ['default', sprintf('mongodb://%s:%d', \MongoClient::DEFAULT_HOST, \MongoClient::DEFAULT_PORT)];
+ yield ['localhost', 'mongodb://localhost'];
+ yield ['mongodb://localhost', 'mongodb://localhost'];
+ }
+
public function testSerialize()
{
- $this->assertInternalType('string', serialize($this->getClient()));
+ $this->assertIsString(serialize($this->getClient()));
}
public function testGetDb()
@@ -58,7 +74,7 @@ public function testGetHosts()
{
$client = $this->getClient();
$hosts = $client->getHosts();
- $this->assertArraySubset(
+ $this->assertMatches(
[
'localhost:27017;-;.;' . getmypid() => [
'host' => 'localhost',
@@ -74,7 +90,7 @@ public function testGetHosts()
public function testGetHostsExceptionHandling()
{
$this->expectException(\MongoConnectionException::class);
- $this->expectExceptionMessageRegExp('/fake_host/');
+ $this->expectErrorMessageMatches('/fake_host/');
$client = $this->getClient(null, 'mongodb://fake_host');
$client->getHosts();
@@ -246,7 +262,7 @@ public function testConnectWithUsernameAndPassword()
$collection->insert($document);
}
-
+
public function testConnectWithUsernameAndPasswordInConnectionUrl()
{
$this->expectException(\MongoConnectionException::class);
@@ -260,6 +276,13 @@ public function testConnectWithUsernameAndPasswordInConnectionUrl()
$collection->insert($document);
}
+ public function testConnectionUriOptionIntegerTypeCasting()
+ {
+ $client = new \MongoClient('mongodb://localhost/db?w=0&wtimeout=0', ['connect' => false]);
+
+ $this->assertSame(['w' => 0, 'wtimeout' => 0], $client->getWriteConcern());
+ }
+
/**
* @param array $options
* @return string
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCodeTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCodeTest.php
index b55568f9..11a593f6 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCodeTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCodeTest.php
@@ -4,6 +4,7 @@
use Alcaeus\MongoDbAdapter\Tests\TestCase;
use Alcaeus\MongoDbAdapter\TypeInterface;
+use ReflectionProperty;
/**
* @author alcaeus
@@ -13,23 +14,49 @@ class MongoCodeTest extends TestCase
public function testCreate()
{
$code = new \MongoCode('code', ['scope' => 'bleh']);
- $this->assertAttributeSame('code', 'code', $code);
- $this->assertAttributeSame(['scope' => 'bleh'], 'scope', $code);
- $this->assertSame('code', (string)$code);
+ $this->assertSame('code', $this->getAttributeValue($code, 'code'));
+ $this->assertSame(['scope' => 'bleh'], $this->getAttributeValue($code, 'scope'));
+
+ $this->assertSame('code', (string) $code);
return $code;
}
- /**
- * @depends testCreate
- */
- public function testConvertToBson(\MongoCode $code)
+ public function testCreateWithoutScope()
+ {
+ $code = new \MongoCode('code');
+
+ $this->assertSame('code', $this->getAttributeValue($code, 'code'));
+ $this->assertSame([], $this->getAttributeValue($code, 'scope'));
+
+ $this->assertSame('code', (string) $code);
+
+ return $code;
+ }
+
+ public function testConvertToBson()
+ {
+ $code = new \MongoCode('code', ['scope' => 'bleh']);
+
+ $this->skipTestUnless($code instanceof TypeInterface);
+
+ $bsonCode = $code->toBSONType();
+ $this->assertInstanceOf('MongoDB\BSON\Javascript', $bsonCode);
+ $this->assertSame('code', $bsonCode->getCode());
+ $this->assertEquals((object) ['scope' => 'bleh'], $bsonCode->getScope());
+ }
+
+ public function testConvertToBsonWithoutScope()
{
+ $code = new \MongoCode('code');
+
$this->skipTestUnless($code instanceof TypeInterface);
$bsonCode = $code->toBSONType();
$this->assertInstanceOf('MongoDB\BSON\Javascript', $bsonCode);
+ $this->assertSame('code', $bsonCode->getCode());
+ $this->assertNull($bsonCode->getScope());
}
public function testCreateWithBsonObject()
@@ -39,7 +66,15 @@ public function testCreateWithBsonObject()
$bsonCode = new \MongoDB\BSON\Javascript('code', ['scope' => 'bleh']);
$code = new \MongoCode($bsonCode);
- $this->assertAttributeSame('code', 'code', $code);
- $this->assertAttributeSame(['scope' => 'bleh'], 'scope', $code);
+ $this->assertSame('code', $this->getAttributeValue($code, 'code'));
+ $this->assertSame(['scope' => 'bleh'], $this->getAttributeValue($code, 'scope'));
+ }
+
+ private function getAttributeValue(\MongoCode $code, $attribute)
+ {
+ $property = new ReflectionProperty($code, $attribute);
+ $property->setAccessible(true);
+
+ return $property->getValue($code);
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCollectionTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCollectionTest.php
index 5f533d23..501cbdf0 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCollectionTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCollectionTest.php
@@ -2,10 +2,14 @@
namespace Alcaeus\MongoDbAdapter\Tests\Mongo;
+use ArrayObject;
use MongoDB\BSON\Regex;
use MongoDB\Driver\ReadPreference;
use Alcaeus\MongoDbAdapter\Tests\TestCase;
+use MongoId;
use PHPUnit\Framework\Error\Warning;
+use function extension_loaded;
+use function strcasecmp;
/**
* @author alcaeus
@@ -14,7 +18,7 @@ class MongoCollectionTest extends TestCase
{
public function testSerialize()
{
- $this->assertInternalType('string', serialize($this->getCollection()));
+ $this->assertIsString(serialize($this->getCollection()));
}
public function testGetNestedCollections()
@@ -44,19 +48,17 @@ public function testCreateRecord()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $object);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $object->_id);
$this->assertSame($id, (string) $object->_id);
- $this->assertObjectHasAttribute('foo', $object);
- $this->assertAttributeSame('bar', 'foo', $object);
+ $this->assertNotNull($object->foo);
+ $this->assertSame('bar', $object->foo);
}
public function testInsertInvalidData()
{
// Dirty hack to support both PHPUnit 5.x and 6.x
- $className = class_exists(Warning::class) ? Warning::class : \PHPUnit_Framework_Error_Warning::class;
- $this->expectException($className);
-
- $this->expectExceptionMessage('MongoCollection::insert(): expects parameter 1 to be an array or object, integer given');
+ $this->expectWarning();
+ $this->expectWarningMessage('MongoCollection::insert(): expects parameter 1 to be an array or object, integer given');
$document = 8;
$this->getCollection()->insert($document);
@@ -78,6 +80,34 @@ public function testInsertArrayWithNumericKeys()
$this->assertSame(1, $this->getCollection()->count(['_id' => $document['_id']]));
}
+ /**
+ * @dataProvider emptyIdProvider
+ */
+ public function testInsertArrayWithEmptyIds($id)
+ {
+ $document = ['_id' => $id];
+ $this->getCollection()->insert($document);
+
+ $this->assertSame(1, $this->getCollection()->count(['_id' => $id]));
+ }
+
+ public function emptyIdProvider()
+ {
+ return [
+ 'Zero as string' => ['0'],
+ 'Zero as int' => [0],
+ 'Empty string' => [''],
+ ];
+ }
+
+ public function testInsertArrayWithEmptyId()
+ {
+ $document = ['_id' => ''];
+ $this->getCollection()->insert($document);
+
+ $this->assertSame(1, $this->getCollection()->count(['_id' => $document['_id']]));
+ }
+
public function testInsertEmptyObject()
{
$document = (object) [];
@@ -95,6 +125,15 @@ public function testInsertObjectWithPrivateProperties()
$this->getCollection()->insert($document);
}
+ public function testInsertArrayObjectWithProtectedProperties()
+ {
+ $document = new ArrayObjectWithProtectedProperties(['foo' => 'bar']);
+ $this->getCollection()->insert($document);
+
+ $this->assertInstanceOf('MongoId', $document['_id']);
+ $this->assertEquals(['_id' => $document['_id'], 'foo' => 'bar'], $this->getCollection()->findOne(['_id' => $document['_id']]));
+ }
+
public function testInsertWithInvalidKey()
{
$document = ['*' => 'foo'];
@@ -130,6 +169,20 @@ public function testInsertWithNumericKey()
$this->assertSame(1, $this->getCollection()->count(['foo']));
}
+ public function testInsertWithAlphaNumericKey()
+ {
+ /**
+ * Force the array to store the key as a string "0".
+ * Initialising like ['0' => 'foo'] casts the string to an int.
+ */
+ $document = new \stdClass();
+ $document->{'0'} = 'foo';
+ $document = (array) $document;
+
+ $this->getCollection()->insert($document);
+ $this->assertSame(1, $this->getCollection()->count(['0' => 'foo']));
+ }
+
public function testInsertDuplicate()
{
$collection = $this->getCollection();
@@ -142,7 +195,7 @@ public function testInsertDuplicate()
unset($document['_id']);
$this->expectException(\MongoDuplicateKeyException::class);
- $this->expectExceptionMessageRegExp('/E11000 duplicate key error .* mongo-php-adapter\.test/');
+ $this->expectErrorMessageMatches('/E11000 duplicate key error .* mongo-php-adapter\.test/');
$this->expectExceptionCode(11000);
$collection->insert($document);
}
@@ -196,7 +249,7 @@ public function testInsertMany()
['foo' => 'bar'],
['bar' => 'foo']
];
- $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents));
+ $this->assertMatches($expected, $this->getCollection()->batchInsert($documents));
foreach ($documents as $document) {
$this->assertInstanceOf('MongoId', $document['_id']);
@@ -218,7 +271,7 @@ public function testInsertManyWithNonNumericKeys()
'a' => ['foo' => 'bar'],
'b' => ['bar' => 'foo']
];
- $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents));
+ $this->assertMatches($expected, $this->getCollection()->batchInsert($documents));
$newCollection = $this->getCheckDatabase()->selectCollection('test');
$this->assertSame(2, $newCollection->count());
@@ -238,7 +291,7 @@ public function testBatchInsertContinuesOnError()
8,
'b' => ['bar' => 'foo']
];
- $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents, ['continueOnError' => true]));
+ $this->assertMatches($expected, $this->getCollection()->batchInsert($documents, ['continueOnError' => true]));
$newCollection = $this->getCheckDatabase()->selectCollection('test');
$this->assertSame(1, $newCollection->count());
@@ -250,7 +303,7 @@ public function testBatchInsertException()
$documents = [['_id' => $id, 'foo' => 'bar'], ['_id' => $id, 'foo' => 'bleh']];
$this->expectException(\MongoDuplicateKeyException::class);
- $this->expectExceptionMessageRegExp('/E11000 duplicate key error .* mongo-php-adapter.test.*_id_/');
+ $this->expectErrorMessageMatches('/E11000 duplicate key error .* mongo-php-adapter.test.*_id_/');
$this->expectExceptionCode(11000);
$this->getCollection()->batchInsert($documents);
@@ -362,7 +415,7 @@ public function testUpdateReplaceOne()
public function testUpdateReplaceMultiple()
{
$this->expectException(\MongoWriteConcernException::class);
- $this->expectExceptionMessageRegExp('/multi update only works with \$ operators/', 9);
+ $this->expectErrorMessageMatches('/multi update only works with \$ operators/', 9);
$this->getCollection()->update(['foo' => 'bar'], ['foo' => 'foo'], ['multiple' => true]);
}
@@ -552,7 +605,7 @@ public function testFindWithProjection($projection)
foreach ($cursor as $document) {
$this->assertCount(2, $document);
$this->assertArrayHasKey('_id', $document);
- $this->assertArraySubset(['bar' => 'bar'], $document);
+ $this->assertMatches(['bar' => 'bar'], $document);
}
}
@@ -614,7 +667,7 @@ public function testFindWithProjectionExcludeId($projection)
foreach ($cursor as $document) {
$this->assertCount(1, $document);
$this->assertArrayNotHasKey('_id', $document);
- $this->assertArraySubset(['bar' => 'bar'], $document);
+ $this->assertMatches(['bar' => 'bar'], $document);
}
}
@@ -714,7 +767,7 @@ public function testFindOneWithProjection()
$document = $this->getCollection()->findOne(['foo' => 'foo'], ['bar' => true]);
$this->assertCount(2, $document);
- $this->assertArraySubset(['bar' => 'bar'], $document);
+ $this->assertMatches(['bar' => 'bar'], $document);
}
public function testFindOneWithLegacyProjection()
@@ -724,7 +777,7 @@ public function testFindOneWithLegacyProjection()
$document = $this->getCollection()->findOne(['foo' => 'foo'], ['bar']);
$this->assertCount(2, $document);
- $this->assertArraySubset(['bar' => 'bar'], $document);
+ $this->assertMatches(['bar' => 'bar'], $document);
}
public function testFindOneNotFound()
@@ -748,7 +801,7 @@ public function testDistinct()
$this->prepareData();
$values = $this->getCollection()->distinct('foo');
- $this->assertInternalType('array', $values);
+ $this->assertIsArray($values);
sort($values);
$this->assertEquals(['bar', 'foo'], $values);
@@ -759,7 +812,7 @@ public function testDistinctWithQuery()
$this->prepareData();
$values = $this->getCollection()->distinct('foo', ['foo' => 'bar']);
- $this->assertInternalType('array', $values);
+ $this->assertIsArray($values);
$this->assertEquals(['bar'], $values);
}
@@ -791,6 +844,8 @@ public function testDistinctWithIdQuery()
public function testAggregate()
{
+ $this->skipTestIf(extension_loaded('mongo'));
+
$collection = $this->getCollection();
$this->prepareData();
@@ -807,8 +862,8 @@ public function testAggregate()
]
];
- $result = $collection->aggregate($pipeline);
- $this->assertInternalType('array', $result);
+ $result = $collection->aggregate($pipeline, ['cursor' => true]);
+ $this->assertIsArray($result);
$this->assertArrayHasKey('result', $result);
$this->assertEquals([
@@ -819,6 +874,8 @@ public function testAggregate()
public function testAggregateWithMultiplePilelineOperatorsAsArguments()
{
+ $this->skipTestIf(version_compare($this->getServerVersion(), '3.6.0', '>='), 'Test does not apply to MongoDB >= 3.6.');
+
$collection = $this->getCollection();
$this->prepareData();
@@ -842,7 +899,7 @@ public function testAggregateWithMultiplePilelineOperatorsAsArguments()
$this->fail($msg);
}
- $this->assertInternalType('array', $result);
+ $this->assertIsArray($result);
$this->assertArrayHasKey('result', $result);
$this->assertEquals([
@@ -853,6 +910,8 @@ public function testAggregateWithMultiplePilelineOperatorsAsArguments()
public function testAggregateInvalidPipeline()
{
+ $this->skipTestIf(extension_loaded('mongo'));
+
$collection = $this->getCollection();
$pipeline = [
@@ -863,7 +922,7 @@ public function testAggregateInvalidPipeline()
$this->expectException(\MongoResultException::class);
$this->expectExceptionMessage('Unrecognized pipeline stage name');
- $collection->aggregate($pipeline);
+ $collection->aggregate($pipeline, ['cursor' => true]);
}
public function testAggregateTimeoutException()
@@ -886,7 +945,7 @@ public function testAggregateTimeoutException()
]
];
- $collection->aggregate($pipeline, ['maxTimeMS' => 1]);
+ $collection->aggregate($pipeline, ['maxTimeMS' => 1, 'cursor' => true]);
}
public function testAggregateCursor()
@@ -930,7 +989,7 @@ public function testReadPreference()
$this->assertSame(['type' => \MongoClient::RP_SECONDARY_PREFERRED, 'tagsets' => [['a' => 'b']]], $collection->getReadPreference());
$this->assertTrue($collection->setSlaveOkay(false));
- $this->assertArraySubset(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
+ $this->assertMatches(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
}
public function testReadPreferenceIsSetInDriver()
@@ -1012,10 +1071,9 @@ public function testSaveInsert()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $object);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $object->_id);
$this->assertSame($id, (string) $object->_id);
- $this->assertObjectHasAttribute('foo', $object);
- $this->assertAttributeSame('bar', 'foo', $object);
+ $this->assertSame('bar', $object->foo);
}
public function testRemoveOne()
@@ -1055,13 +1113,13 @@ public function testSaveUpdate()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $object);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $object->_id);
$this->assertSame($id, (string) $object->_id);
- $this->assertObjectHasAttribute('foo', $object);
- $this->assertAttributeSame('foo', 'foo', $object);
+ $this->assertSame('foo', $object->foo);
}
- public function testSavingShouldReplaceTheWholeDocument() {
+ public function testSavingShouldReplaceTheWholeDocument()
+ {
$id = '54203e08d51d4a1f868b456e';
$collection = $this->getCollection();
@@ -1076,7 +1134,7 @@ public function testSavingShouldReplaceTheWholeDocument() {
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertObjectNotHasAttribute('foo', $object);
+ $this->assertArrayNotHasKey('bar', $object);
}
public function testSaveDuplicate()
@@ -1339,8 +1397,8 @@ public function testDeleteIndexUsingIndexName()
$expected['code'] = 27;
}
- // Using assertArraySubset because newer versions (3.4.7?) also return `codeName`
- $this->assertArraySubset($expected, $this->getCollection()->deleteIndex('bar'));
+ // Using assertMatches because newer versions (3.4.7?) also return `codeName`
+ $this->assertMatches($expected, $this->getCollection()->deleteIndex('bar'));
$this->assertCount(2, iterator_to_array($newCollection->listIndexes()));
}
@@ -1390,17 +1448,14 @@ public function testDeleteIndexes()
public function testDeleteIndexesForNonExistingCollection()
{
- $expected = [
- 'ok' => 0.0,
- 'errmsg' => 'ns not found',
- ];
+ $result = $this->getCollection('nonExisting')->deleteIndexes();
+ $this->assertSame(0.0, $result['ok']);
+ $this->assertMatchesRegularExpression('#ns not found#', $result['errmsg']);
if (version_compare($this->getServerVersion(), '3.4.0', '>=')) {
+ $this->assertSame(26, $result['code']);
$expected['code'] = 26;
}
-
- // Using assertArraySubset because newer versions (3.4.7?) also return `codeName`
- $this->assertArraySubset($expected, $this->getCollection('nonExisting')->deleteIndexes());
}
public function dataGetIndexInfo()
@@ -1551,7 +1606,7 @@ public function testFindAndModifyUpdate()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeSame('foo', 'foo', $object);
+ $this->assertSame('foo', $object->foo);
}
public function testFindAndModifyUpdateWithUpdateOptions()
@@ -1576,8 +1631,8 @@ public function testFindAndModifyUpdateWithUpdateOptions()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeSame('foo', 'bar', $object);
- $this->assertObjectNotHasAttribute('foo', $object);
+ $this->assertSame('foo', $object->bar);
+ $this->assertArrayNotHasKey('foo', $object);
}
public function testFindAndModifyWithUpdateParamAndOption()
@@ -1605,8 +1660,8 @@ public function testFindAndModifyWithUpdateParamAndOption()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeSame('foobar', 'foo', $object);
- $this->assertObjectNotHasAttribute('bar', $object);
+ $this->assertSame('foobar', $object->foo);
+ $this->assertArrayNotHasKey('bar', $object);
}
public function testFindAndModifyUpdateReplace()
@@ -1627,8 +1682,8 @@ public function testFindAndModifyUpdateReplace()
$object = $newCollection->findOne();
$this->assertNotNull($object);
- $this->assertAttributeSame('boo', 'foo', $object);
- $this->assertObjectNotHasAttribute('bar', $object);
+ $this->assertSame('boo', $object->foo);
+ $this->assertArrayNotHasKey('bar', $object);
}
public function testFindAndModifyUpdateReturnNew()
@@ -1669,6 +1724,8 @@ public function testFindAndModifyWithFields()
public function testGroup()
{
+ $this->skipTestIf(version_compare($this->getServerVersion(), '4.2.0', '>='), 'Test does not apply to MongoDB >= 4.2.');
+
$collection = $this->getCollection();
$document1 = ['a' => 2];
@@ -1684,7 +1741,7 @@ public function testGroup()
$result = $collection->group($keys, $initial, $reduce, $condition);
- $this->assertArraySubset(
+ $this->assertMatches(
[
'retval' => [['count' => 1.0]],
'count' => 1.0,
@@ -1740,7 +1797,7 @@ public function testMapReduce()
'map' => new \MongoCode($map),
'reduce' => new \MongoCode($reduce),
'query' => (object) [],
- 'out' => ['inline' => true],
+ 'out' => ['inline' => 1],
'finalize' => new \MongoCode($finalize),
];
@@ -1765,8 +1822,12 @@ public function testMapReduce()
],
];
+ usort($result['results'], function ($a, $b) {
+ return strcasecmp($a['_id'], $b['_id']);
+ });
+
$this->assertSame(1.0, $result['ok']);
- $this->assertSame($expected, $result['results']);
+ $this->assertEquals($expected, $result['results']);
}
public function testFindAndModifyResultException()
@@ -1828,12 +1889,11 @@ public function testValidate()
$collection->insert($document);
$result = $collection->validate();
- $this->assertArraySubset(
+ $this->assertMatches(
[
'ns' => 'mongo-php-adapter.test',
'nrecords' => 1,
'nIndexes' => 1,
- 'keysPerIndex' => ['mongo-php-adapter.test.$_id_' => 1],
'valid' => true,
'errors' => [],
],
@@ -1850,7 +1910,7 @@ public function testDrop()
'nIndexesWas' => 1,
'ok' => 1.0
];
- $this->assertSame($expected, $this->getCollection()->drop());
+ $this->assertEquals($expected, $this->getCollection()->drop());
}
public function testEmptyCollectionName()
@@ -1976,3 +2036,8 @@ class PrivatePropertiesStub
{
private $foo = 'bar';
}
+
+class ArrayObjectWithProtectedProperties extends ArrayObject
+{
+ protected $something = 'baz';
+}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCommandCursorTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCommandCursorTest.php
index e5a8d5c3..3d557608 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCommandCursorTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCommandCursorTest.php
@@ -2,6 +2,7 @@
namespace Alcaeus\MongoDbAdapter\Tests\Mongo;
+use MongoCursorInterface;
use MongoDB\Database;
use MongoDB\Driver\ReadPreference;
use Alcaeus\MongoDbAdapter\Tests\TestCase;
@@ -15,7 +16,7 @@ public function testSerialize()
{
$this->prepareData();
$cursor = $this->getCollection()->aggregateCursor([['$match' => ['foo' => 'bar']]]);
- $this->assertInternalType('string', serialize($cursor));
+ $this->assertIsString(serialize($cursor));
}
public function testInfo()
@@ -58,7 +59,7 @@ public function testInfo()
'connection_type_desc' => 'STANDALONE',
];
- $this->assertArraySubset($expected, $cursor->info());
+ $this->assertMatches($expected, $cursor->info());
$i = 0;
foreach ($array as $key => $value) {
@@ -136,7 +137,7 @@ public function dataCommandAppliesCorrectReadPreference()
'mapReduceWithOutInline' => [
[
'mapReduce' => (string) $this->getCollection(),
- 'out' => ['inline' => true],
+ 'out' => ['inline' => 1],
],
ReadPreference::RP_SECONDARY,
],
@@ -202,4 +203,12 @@ public function dataCommandAppliesCorrectReadPreference()
],
];
}
+
+ public function testInterfaces()
+ {
+ $this->prepareData();
+ $cursor = $this->getCollection()->aggregateCursor([['$match' => ['foo' => 'bar']]]);
+
+ $this->assertInstanceOf(MongoCursorInterface::class, $cursor);
+ }
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCursorTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCursorTest.php
index abc1bdde..d2af27fb 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCursorTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoCursorTest.php
@@ -4,6 +4,8 @@
use Alcaeus\MongoDbAdapter\Tests\TestCase;
use Alcaeus\MongoDbAdapter\TypeConverter;
+use Countable;
+use MongoCursorInterface;
use MongoDB\Driver\ReadPreference;
use MongoDB\Model\BSONDocument;
use MongoDB\Operation\Find;
@@ -17,7 +19,7 @@ public function testSerialize()
{
$this->prepareData();
$cursor = $this->getCollection()->find(['foo' => 'bar']);
- $this->assertInternalType('string', serialize($cursor));
+ $this->assertIsString(serialize($cursor));
}
public function testCursorConvertsTypes()
@@ -200,7 +202,8 @@ public function testCursorAppliesOptions($checkOptionCallback, \Closure $applyOp
public static function getCursorOptions()
{
- function getMissingOptionCallback($optionName) {
+ function getMissingOptionCallback($optionName)
+ {
return function ($value) use ($optionName) {
return
is_array($value) &&
@@ -208,7 +211,8 @@ function getMissingOptionCallback($optionName) {
};
}
- function getBasicCheckCallback($expected, $optionName) {
+ function getBasicCheckCallback($expected, $optionName)
+ {
return function ($value) use ($expected, $optionName) {
return
is_array($value) &&
@@ -217,7 +221,8 @@ function getBasicCheckCallback($expected, $optionName) {
};
}
- function getModifierCheckCallback($expected, $modifierName) {
+ function getModifierCheckCallback($expected, $modifierName)
+ {
return function ($value) use ($expected, $modifierName) {
return
is_array($value) &&
@@ -429,23 +434,23 @@ public function testExplain()
'parsedQuery' => [
'foo' => ['$eq' => 'bar']
],
- 'winningPlan' => [],
- 'rejectedPlans' => [],
+ 'winningPlan' => ['$$exists' => true],
+ 'rejectedPlans' => ['$$exists' => true],
],
'executionStats' => [
'executionSuccess' => true,
'nReturned' => 1,
'totalKeysExamined' => 0,
'totalDocsExamined' => 3,
- 'executionStages' => [],
- 'allPlansExecution' => [],
+ 'executionStages' => ['$$exists' => true],
+ 'allPlansExecution' => ['$$exists' => true],
],
'serverInfo' => [
'port' => 27017,
],
];
- $this->assertArraySubset($expected, $cursor->explain());
+ $this->assertMatches($expected, $cursor->explain());
}
public function testExplainWithEmptyProjection()
@@ -463,23 +468,23 @@ public function testExplainWithEmptyProjection()
'parsedQuery' => [
'foo' => ['$eq' => 'bar']
],
- 'winningPlan' => [],
- 'rejectedPlans' => [],
+ 'winningPlan' => ['$$exists' => true],
+ 'rejectedPlans' => ['$$exists' => true],
],
'executionStats' => [
'executionSuccess' => true,
'nReturned' => 2,
'totalKeysExamined' => 0,
'totalDocsExamined' => 3,
- 'executionStages' => [],
- 'allPlansExecution' => [],
+ 'executionStages' => ['$$exists' => true],
+ 'allPlansExecution' => ['$$exists' => true],
],
'serverInfo' => [
'port' => 27017,
],
];
- $this->assertArraySubset($expected, $cursor->explain());
+ $this->assertMatches($expected, $cursor->explain());
}
public function testExplainConvertsQuery()
@@ -494,23 +499,36 @@ public function testExplainConvertsQuery()
'plannerVersion' => 1,
'namespace' => 'mongo-php-adapter.test',
'indexFilterSet' => false,
- 'winningPlan' => [],
- 'rejectedPlans' => [],
+ 'winningPlan' => ['$$exists' => true],
+ 'rejectedPlans' => ['$$exists' => true],
],
'executionStats' => [
'executionSuccess' => true,
'nReturned' => 2,
'totalKeysExamined' => 0,
'totalDocsExamined' => 3,
- 'executionStages' => [],
- 'allPlansExecution' => [],
+ 'executionStages' => ['$$exists' => true],
+ 'allPlansExecution' => ['$$exists' => true],
],
'serverInfo' => [
'port' => 27017,
],
];
- $this->assertArraySubset($expected, $cursor->explain());
+ $this->assertMatches($expected, $cursor->explain());
+ }
+
+ public function testInterfaces()
+ {
+ $collection = $this->getCollection();
+ $cursor = $collection->find();
+
+ $this->assertInstanceOf(MongoCursorInterface::class, $cursor);
+
+ // The countable interface is necessary for compatibility with PHP 7.3+, but not implemented by MongoCursor
+ if (! extension_loaded('mongo')) {
+ $this->assertInstanceOf(Countable::class, $cursor);
+ }
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBRefTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBRefTest.php
index ba39c8ca..fb5000b8 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBRefTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBRefTest.php
@@ -85,7 +85,7 @@ public function testGet()
$db->selectCollection('test')->insert($document);
$fetchedRef = \MongoDBRef::get($db, ['$ref' => 'test', '$id' => $id]);
- $this->assertInternalType('array', $fetchedRef);
+ $this->assertIsArray($fetchedRef);
$this->assertEquals($document, $fetchedRef);
}
@@ -99,7 +99,7 @@ public function testGetThroughMongoDB()
$db->selectCollection('test')->insert($document);
$fetchedRef = $db->getDBRef(['$ref' => 'test', '$id' => $id]);
- $this->assertInternalType('array', $fetchedRef);
+ $this->assertIsArray($fetchedRef);
$this->assertEquals($document, $fetchedRef);
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBTest.php
index 1636341e..9b7d18db 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDBTest.php
@@ -12,7 +12,7 @@ class MongoDBTest extends TestCase
{
public function testSerialize()
{
- $this->assertInternalType('string', serialize($this->getDatabase()));
+ $this->assertIsString(serialize($this->getDatabase()));
}
public function testEmptyDatabaseName()
@@ -126,8 +126,8 @@ public function testCommandError()
'code' => 13,
];
- // Using assertArraySubset because newer versions (3.4.7?) also return `codeName`
- $this->assertArraySubset($expected, $db->command(['listDatabases' => 1]));
+ // Using assertMatches because newer versions (3.4.7?) also return `codeName`
+ $this->assertMatches($expected, $db->command(['listDatabases' => 1]));
}
public function testCommandCursorTimeout()
@@ -164,7 +164,7 @@ public function testReadPreference()
$this->assertTrue($database->setSlaveOkay(false));
// Only test a subset since we don't keep tagsets around for RP_PRIMARY
- $this->assertArraySubset(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
+ $this->assertMatches(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
}
public function testReadPreferenceIsSetInDriver()
@@ -180,7 +180,6 @@ public function testReadPreferenceIsSetInDriver()
$this->assertSame(ReadPreference::RP_SECONDARY, $readPreference->getMode());
$this->assertSame([['a' => 'b']], $readPreference->getTagSets());
-
}
public function testReadPreferenceIsInherited()
@@ -241,6 +240,8 @@ public function testForceError()
public function testExecute()
{
+ $this->skipTestIf(version_compare($this->getServerVersion(), '4.2.0', '>='), 'Eval no longer works on MongoDB 4.2.0 and newer');
+
$db = $this->getDatabase();
$document = ['foo' => 'bar'];
$this->getCollection()->insert($document);
@@ -291,7 +292,7 @@ public function testGetCollectionInfo()
],
];
}
- $this->assertEquals($expected, $collectionInfo);
+ $this->assertMatches($expected, $collectionInfo);
return;
}
}
@@ -385,11 +386,13 @@ public function testDropCollection()
'nIndexesWas' => 1,
'ok' => 1.0
];
- $this->assertSame($expected, $this->getDatabase()->dropCollection('test'));
+ $this->assertEquals($expected, $this->getDatabase()->dropCollection('test'));
}
public function testRepair()
{
+ $this->skipTestIf(version_compare($this->getServerVersion(), '4.2.0', '>='), 'The "repairDatabase" has been removed in MongoDB 4.2.0');
+
$this->assertSame(['ok' => 1.0], $this->getDatabase()->repair());
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDateTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDateTest.php
index db58c4bb..6f75386b 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDateTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDateTest.php
@@ -17,7 +17,8 @@ public function testTimeZoneDoesNotAlterReturnedDateTime()
ini_set("date.timezone", "UTC");
// Today at 8h 8m 8s
- $timestamp = mktime (8, 8, 8); $date = new \MongoDate($timestamp);
+ $timestamp = mktime(8, 8, 8);
+ $date = new \MongoDate($timestamp);
$this->assertSame('08:08:08', $date->toDateTime()->format("H:i:s"));
@@ -31,8 +32,8 @@ public function testTimeZoneDoesNotAlterReturnedDateTime()
public function testCreate()
{
$date = new \MongoDate(1234567890, 123456);
- $this->assertAttributeSame(1234567890, 'sec', $date);
- $this->assertAttributeSame(123000, 'usec', $date);
+ $this->assertSame(1234567890, $date->sec);
+ $this->assertSame(123000, $date->usec);
$this->assertSame('0.12300000 1234567890', (string) $date);
$dateTime = $date->toDateTime();
@@ -67,8 +68,8 @@ public function testConvertToBson(\MongoDate $date)
public function testCreateWithString()
{
$date = new \MongoDate('1234567890', '123456');
- $this->assertAttributeSame(1234567890, 'sec', $date);
- $this->assertAttributeSame(123000, 'usec', $date);
+ $this->assertSame(1234567890, $date->sec);
+ $this->assertSame(123000, $date->usec);
}
public function testCreateWithBsonDate()
@@ -78,15 +79,15 @@ public function testCreateWithBsonDate()
$bsonDate = new \MongoDB\BSON\UTCDateTime(1234567890123);
$date = new \MongoDate($bsonDate);
- $this->assertAttributeSame(1234567890, 'sec', $date);
- $this->assertAttributeSame(123000, 'usec', $date);
+ $this->assertSame(1234567890, $date->sec);
+ $this->assertSame(123000, $date->usec);
}
public function testSupportMillisecondsWithLeadingZeroes()
{
$date = new \MongoDate('1234567890', '012345');
- $this->assertAttributeSame(1234567890, 'sec', $date);
- $this->assertAttributeSame(12000, 'usec', $date);
+ $this->assertSame(1234567890, $date->sec);
+ $this->assertSame(12000, $date->usec);
$this->assertSame('0.01200000 1234567890', (string) $date);
$dateTime = $date->toDateTime();
@@ -94,4 +95,33 @@ public function testSupportMillisecondsWithLeadingZeroes()
$this->assertSame(1234567890, $dateTime->getTimestamp());
$this->assertSame('012000', $dateTime->format('u'));
}
+
+ public function testDSTTransitionDoesNotProduceWrongResults()
+ {
+ $initialTZ = ini_get("date.timezone");
+
+ ini_set("date.timezone", "Europe/Madrid");
+
+ $date = new \MongoDate(1603584000);
+ $dateTime = $date->toDateTime();
+
+ $this->assertSame(1603584000, $dateTime->getTimestamp());
+
+ ini_set("date.timezone", $initialTZ);
+ }
+
+ public function testDSTTransitionDoesNotProduceWrongResultsWithMicroSeconds()
+ {
+ $initialTZ = ini_get("date.timezone");
+
+ ini_set("date.timezone", "Europe/Madrid");
+
+ $date = new \MongoDate(1603584000, 123456);
+ $dateTime = $date->toDateTime();
+
+ $this->assertSame(1603584000, $dateTime->getTimestamp());
+ $this->assertSame('123000', $dateTime->format('u'));
+
+ ini_set("date.timezone", $initialTZ);
+ }
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDeleteBatchTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDeleteBatchTest.php
index 1e41e31a..6502ae95 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDeleteBatchTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoDeleteBatchTest.php
@@ -9,7 +9,7 @@ class MongoDeleteBatchTest extends TestCase
public function testSerialize()
{
$batch = new \MongoDeleteBatch($this->getCollection());
- $this->assertInternalType('string', serialize($batch));
+ $this->assertIsString(serialize($batch));
}
public function testDeleteOne()
@@ -58,6 +58,29 @@ public function testDeleteMany()
$this->assertSame(0, $newCollection->count());
}
+ public function testDeleteManyWithoutAck()
+ {
+ $collection = $this->getCollection();
+ $batch = new \MongoDeleteBatch($collection);
+
+ $document = ['foo' => 'bar'];
+ $collection->insert($document);
+ unset($document['_id']);
+ $collection->insert($document);
+
+ $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'limit' => 0]));
+
+ $expected = [
+ 'nRemoved' => 0,
+ 'ok' => true,
+ ];
+
+ $this->assertSame($expected, $batch->execute(['w' => 0]));
+
+ $newCollection = $this->getCheckDatabase()->selectCollection('test');
+ $this->assertSame(0, $newCollection->count());
+ }
+
public function testValidateItem()
{
$collection = $this->getCollection();
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSCursorTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSCursorTest.php
index 81c815b4..afbe9c4e 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSCursorTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSCursorTest.php
@@ -3,6 +3,7 @@
namespace Alcaeus\MongoDbAdapter\Tests\Mongo;
use Alcaeus\MongoDbAdapter\Tests\TestCase;
+use Countable;
class MongoGridFSCursorTest extends TestCase
{
@@ -13,7 +14,7 @@ public function testSerialize()
$gridfs->storeBytes('bar', ['filename' => 'bar.txt']);
$cursor = $gridfs->find(['filename' => 'foo.txt']);
- $this->assertInternalType('string', serialize($cursor));
+ $this->assertIsString(serialize($cursor));
}
public function testCursorItems()
@@ -25,11 +26,11 @@ public function testCursorItems()
$cursor = $gridfs->find(['filename' => 'foo.txt']);
$this->assertCount(1, $cursor);
foreach ($cursor as $key => $value) {
- $this->assertSame((string)$id, $key);
+ $this->assertSame((string) $id, $key);
$this->assertInstanceOf('MongoGridFSFile', $value);
$this->assertSame('foo', $value->getBytes());
- $this->assertArraySubset([
+ $this->assertMatches([
'filename' => 'foo.txt',
'chunkSize' => 261120,
'length' => 3,
@@ -37,4 +38,16 @@ public function testCursorItems()
], $value->file);
}
}
+
+ public function testInterfaces()
+ {
+ $this->skipTestIf(extension_loaded('mongo'));
+
+ $gridfs = $this->getGridFS();
+ $id = $gridfs->storeBytes('foo', ['filename' => 'foo.txt']);
+ $gridfs->storeBytes('bar', ['filename' => 'bar.txt']);
+
+ $cursor = $gridfs->find(['filename' => 'foo.txt']);
+ $this->assertInstanceOf(Countable::class, $cursor);
+ }
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSFileTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSFileTest.php
index d718b97b..119bf9fd 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSFileTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSFileTest.php
@@ -12,14 +12,14 @@ public function testSerialize()
$file = $this->getGridFS()->findOne(['filename' => 'foo']);
$this->assertInstanceOf(\MongoGridFSFile::class, $file);
- $this->assertInternalType('string', serialize($file));
+ $this->assertIsString(serialize($file));
}
public function testFileProperty()
{
$file = $this->getFile();
$this->assertArrayHasKey('_id', $file->file);
- $this->assertArraySubset(
+ $this->assertMatches(
[
'length' => 666,
'filename' => 'file',
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSTest.php
index 4d163b4f..044ee86c 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoGridFSTest.php
@@ -8,7 +8,7 @@ class MongoGridFSTest extends TestCase
{
public function testSerialize()
{
- $this->assertInternalType('string', serialize($this->getGridFS()));
+ $this->assertIsString(serialize($this->getGridFS()));
}
public function testChunkProperty()
@@ -70,33 +70,29 @@ public function testStoringData()
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $record);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $record->_id);
$this->assertSame((string) $id, (string) $record->_id);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
- $this->assertObjectHasAttribute('length', $record);
- $this->assertAttributeSame(4, 'length', $record);
- $this->assertObjectHasAttribute('chunkSize', $record);
- $this->assertAttributeSame(2, 'chunkSize', $record);
- $this->assertObjectHasAttribute('md5', $record);
- $this->assertAttributeSame('e2fc714c4727ee9395f324cd2e7f331f', 'md5', $record);
+ $this->assertSame('bar', $record->foo);
+ $this->assertSame(4, $record->length);
+ $this->assertSame(2, $record->chunkSize);
+ $this->assertSame('e2fc714c4727ee9395f324cd2e7f331f', $record->md5);
$chunksCursor = $newChunksCollection->find([], ['sort' => ['n' => 1]]);
$chunks = iterator_to_array($chunksCursor);
$firstChunk = $chunks[0];
$this->assertNotNull($firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', 'files_id', $firstChunk);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $firstChunk->files_id);
$this->assertSame((string) $id, (string) $firstChunk->files_id);
- $this->assertAttributeSame(0, 'n', $firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\Binary', 'data', $firstChunk);
+ $this->assertSame(0, $firstChunk->n);
+ $this->assertInstanceOf('MongoDB\BSON\Binary', $firstChunk->data);
$this->assertSame('ab', (string) $firstChunk->data->getData());
$secondChunck = $chunks[1];
$this->assertNotNull($secondChunck);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', 'files_id', $secondChunck);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $secondChunck->files_id);
$this->assertSame((string) $id, (string) $secondChunck->files_id);
- $this->assertAttributeSame(1, 'n', $secondChunck);
- $this->assertAttributeInstanceOf('MongoDB\BSON\Binary', 'data', $secondChunck);
+ $this->assertSame(1, $secondChunck->n);
+ $this->assertInstanceOf('MongoDB\BSON\Binary', $secondChunck->data);
$this->assertSame('cd', (string) $secondChunck->data->getData());
}
@@ -164,29 +160,24 @@ public function testStoreFile()
$size = filesize($filename);
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $record);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $record->_id);
$this->assertSame((string) $id, (string) $record->_id);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
- $this->assertObjectHasAttribute('length', $record);
- $this->assertAttributeSame($size, 'length', $record);
- $this->assertObjectHasAttribute('chunkSize', $record);
- $this->assertAttributeSame(100, 'chunkSize', $record);
- $this->assertObjectHasAttribute('md5', $record);
- $this->assertAttributeSame($md5, 'md5', $record);
- $this->assertObjectHasAttribute('filename', $record);
- $this->assertAttributeSame($filename, 'filename', $record);
-
- $numberOfChunks = (int)ceil($size / 100);
+ $this->assertSame('bar', $record->foo);
+ $this->assertSame($size, $record->length);
+ $this->assertSame(100, $record->chunkSize);
+ $this->assertSame($md5, $record->md5);
+ $this->assertSame($filename, $record->filename);
+
+ $numberOfChunks = (int) ceil($size / 100);
$this->assertSame($numberOfChunks, $newChunksCollection->count());
$expectedContent = substr(file_get_contents(__FILE__), 0, 100);
$firstChunk = $newChunksCollection->findOne([], ['sort' => ['n' => 1]]);
$this->assertNotNull($firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', 'files_id', $firstChunk);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $firstChunk->files_id);
$this->assertSame((string) $id, (string) $firstChunk->files_id);
- $this->assertAttributeSame(0, 'n', $firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\Binary', 'data', $firstChunk);
+ $this->assertSame(0, $firstChunk->n);
+ $this->assertInstanceOf('MongoDB\BSON\Binary', $firstChunk->data);
$this->assertSame($expectedContent, (string) $firstChunk->data->getData());
}
@@ -209,29 +200,24 @@ public function testStoreFileResource()
$filename = basename(__FILE__);
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $record);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $record->_id);
$this->assertSame((string) $id, (string) $record->_id);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
- $this->assertObjectHasAttribute('length', $record);
- $this->assertAttributeSame($size, 'length', $record);
- $this->assertObjectHasAttribute('chunkSize', $record);
- $this->assertAttributeSame(100, 'chunkSize', $record);
- $this->assertObjectHasAttribute('md5', $record);
- $this->assertAttributeSame($md5, 'md5', $record);
- $this->assertObjectHasAttribute('filename', $record);
- $this->assertAttributeSame('test.php', 'filename', $record);
-
- $numberOfChunks = (int)ceil($size / 100);
+ $this->assertSame('bar', $record->foo);
+ $this->assertSame($size, $record->length);
+ $this->assertSame(100, $record->chunkSize);
+ $this->assertSame($md5, $record->md5);
+ $this->assertSame('test.php', $record->filename);
+
+ $numberOfChunks = (int) ceil($size / 100);
$this->assertSame($numberOfChunks, $newChunksCollection->count());
$expectedContent = substr(file_get_contents(__FILE__), 0, 100);
$firstChunk = $newChunksCollection->findOne([], ['sort' => ['n' => 1]]);
$this->assertNotNull($firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', 'files_id', $firstChunk);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $firstChunk->files_id);
$this->assertSame((string) $id, (string) $firstChunk->files_id);
- $this->assertAttributeSame(0, 'n', $firstChunk);
- $this->assertAttributeInstanceOf('MongoDB\BSON\Binary', 'data', $firstChunk);
+ $this->assertSame(0, $firstChunk->n);
+ $this->assertInstanceOf('MongoDB\BSON\Binary', $firstChunk->data);
$this->assertSame($expectedContent, (string) $firstChunk->data->getData());
}
@@ -260,20 +246,15 @@ public function testStoreUpload()
$size = filesize(__FILE__);
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $record);
+ $this->assertInstanceOf('MongoDB\BSON\ObjectID', $record->_id);
$this->assertSame((string) $id, (string) $record->_id);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
- $this->assertObjectHasAttribute('length', $record);
- $this->assertAttributeSame($size, 'length', $record);
- $this->assertObjectHasAttribute('chunkSize', $record);
- $this->assertAttributeSame(100, 'chunkSize', $record);
- $this->assertObjectHasAttribute('md5', $record);
- $this->assertAttributeSame($md5, 'md5', $record);
- $this->assertObjectHasAttribute('filename', $record);
- $this->assertAttributeSame('test.php', 'filename', $record);
-
- $numberOfChunks = (int)ceil($size / 100);
+ $this->assertSame('bar', $record->foo);
+ $this->assertSame($size, $record->length);
+ $this->assertSame(100, $record->chunkSize);
+ $this->assertSame($md5, $record->md5);
+ $this->assertSame('test.php', $record->filename);
+
+ $numberOfChunks = (int) ceil($size / 100);
$this->assertSame($numberOfChunks, $newChunksCollection->count());
}
@@ -332,7 +313,7 @@ public function testPut()
$this->assertSame(1, $newCollection->count());
$size = filesize(__FILE__);
- $numberOfChunks = (int)ceil($size / 100);
+ $numberOfChunks = (int) ceil($size / 100);
$this->assertSame($numberOfChunks, $newChunksCollection->count());
}
@@ -346,7 +327,7 @@ public function testStoreByteExceptionWhileInsertingRecord()
$collection->insert($document);
$this->expectException(\MongoGridFSException::class);
- $this->expectExceptionMessageRegExp('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
+ $this->expectErrorMessageMatches('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
$this->expectExceptionCode(11000);
$collection->storeBytes('foo', ['_id' => $id]);
@@ -361,7 +342,7 @@ public function testStoreByteExceptionWhileInsertingChunks()
$collection->chunks->insert($document);
$this->expectException(\MongoGridFSException::class);
- $this->expectExceptionMessageRegExp('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
+ $this->expectErrorMessageMatches('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
$this->expectExceptionCode(11000);
$collection->storeBytes('foo');
@@ -376,7 +357,7 @@ public function testStoreFileExceptionWhileInsertingRecord()
$collection->insert($document);
$this->expectException(\MongoGridFSException::class);
- $this->expectExceptionMessageRegExp('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
+ $this->expectErrorMessageMatches('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
$this->expectExceptionCode(11000);
$collection->storeFile(__FILE__, ['_id' => $id]);
@@ -391,7 +372,7 @@ public function testStoreFileExceptionWhileInsertingChunks()
$collection->chunks->insert($document);
$this->expectException(\MongoGridFSException::class);
- $this->expectExceptionMessageRegExp('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
+ $this->expectErrorMessageMatches('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
$this->expectExceptionCode(11000);
$collection->storeFile(__FILE__);
@@ -406,7 +387,7 @@ public function testStoreFileExceptionWhileUpdatingFileRecord()
$collection->insert($document);
$this->expectException(\MongoGridFSException::class);
- $this->expectExceptionMessageRegExp('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
+ $this->expectErrorMessageMatches('/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
$this->expectExceptionCode(11000);
$collection->storeFile(fopen(__FILE__, 'r'));
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoIdTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoIdTest.php
index 81953bea..1a9bcd32 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoIdTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoIdTest.php
@@ -4,6 +4,7 @@
use Alcaeus\MongoDbAdapter\Tests\TestCase;
use MongoDB\BSON\ObjectID;
+use ReflectionProperty;
/**
* @author alcaeus
@@ -40,12 +41,11 @@ public function testCreateWithString()
$this->assertSame(34335, $id->getPID());
}
- /**
- * @expectedException \MongoException
- * @expectedExceptionMessage Invalid object ID
- */
public function testCreateWithInvalidStringThrowsMongoException()
{
+ $this->expectException('\MongoException');
+ $this->expectExceptionMessage('Invalid object ID');
+
new \MongoId('invalid');
}
@@ -59,7 +59,7 @@ public function testCreateWithObjectId()
$id = new \MongoId($objectId);
$this->assertSame($original, (string) $id);
- $this->assertAttributeNotSame($objectId, 'objectID', $id);
+ $this->assertNotSame($objectId, $this->getAttributeValue($id, 'objectID'));
}
/**
@@ -83,4 +83,12 @@ public static function dataIsValid()
'object' => [false, new \stdClass()],
];
}
+
+ private function getAttributeValue(\MongoId $id, $attribute)
+ {
+ $property = new ReflectionProperty($id, $attribute);
+ $property->setAccessible(true);
+
+ return $property->getValue($id);
+ }
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoInsertBatchTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoInsertBatchTest.php
index 06a47839..a7badfac 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoInsertBatchTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoInsertBatchTest.php
@@ -9,7 +9,7 @@ class MongoInsertBatchTest extends TestCase
public function testSerialize()
{
$batch = new \MongoInsertBatch($this->getCollection());
- $this->assertInternalType('string', serialize($batch));
+ $this->assertIsString(serialize($batch));
}
public function testInsertBatch()
@@ -30,8 +30,28 @@ public function testInsertBatch()
$this->assertSame(2, $newCollection->count());
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
+ $this->assertSame('bar', $record->foo);
+ }
+
+ public function testInsertBatchWithoutAck()
+ {
+ $batch = new \MongoInsertBatch($this->getCollection());
+
+ $this->assertTrue($batch->add(['foo' => 'bar']));
+ $this->assertTrue($batch->add(['bar' => 'foo']));
+
+ $expected = [
+ 'nInserted' => 0,
+ 'ok' => true,
+ ];
+
+ $this->assertSame($expected, $batch->execute(['w' => 0]));
+
+ $newCollection = $this->getCheckDatabase()->selectCollection('test');
+ $this->assertSame(2, $newCollection->count());
+ $record = $newCollection->findOne();
+ $this->assertNotNull($record);
+ $this->assertSame('bar', $record->foo);
}
public function testInsertBatchError()
@@ -60,7 +80,7 @@ public function testInsertBatchError()
} catch (\MongoWriteConcernException $e) {
$this->assertSame('Failed write', $e->getMessage());
$this->assertSame(911, $e->getCode());
- $this->assertArraySubset($expected, $e->getDocument());
+ $this->assertMatches($expected, $e->getDocument());
}
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoLogTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoLogTest.php
index daceb6cb..fda63162 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoLogTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoLogTest.php
@@ -8,7 +8,8 @@ class MongoLogTest extends Testcase
{
public function testSetCallback()
{
- $foo = function() {};
+ $foo = function () {
+ };
$this->assertTrue(\MongoLog::setCallback($foo));
$this->assertSame($foo, \MongoLog::getCallback());
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoRegexTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoRegexTest.php
index 6ae20d44..a515bbe7 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoRegexTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoRegexTest.php
@@ -13,8 +13,8 @@ class MongoRegexTest extends TestCase
public function testCreate()
{
$regex = new \MongoRegex('/abc/i');
- $this->assertAttributeSame('abc', 'regex', $regex);
- $this->assertAttributeSame('i', 'flags', $regex);
+ $this->assertSame('abc', $regex->regex);
+ $this->assertSame('i', $regex->flags);
$this->assertSame('/abc/i', (string) $regex);
@@ -41,7 +41,7 @@ public function testCreateWithBsonType()
$bsonRegex = new \MongoDB\BSON\Regex('abc', 'i');
$regex = new \MongoRegex($bsonRegex);
- $this->assertAttributeSame('abc', 'regex', $regex);
- $this->assertAttributeSame('i', 'flags', $regex);
+ $this->assertSame('abc', $regex->regex);
+ $this->assertSame('i', $regex->flags);
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoTimestampTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoTimestampTest.php
index 16c8f2e4..c9d38ea6 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoTimestampTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoTimestampTest.php
@@ -13,8 +13,8 @@ class MongoTimestampTest extends TestCase
public function testCreate()
{
$timestamp = new \MongoTimestamp(1234567890, 987654321);
- $this->assertAttributeSame(1234567890, 'sec', $timestamp);
- $this->assertAttributeSame(987654321, 'inc', $timestamp);
+ $this->assertSame(1234567890, $timestamp->sec);
+ $this->assertSame(987654321, $timestamp->inc);
$this->assertSame('1234567890', (string) $timestamp);
@@ -38,8 +38,8 @@ public function testCreateWithGlobalInc()
$timestamp1 = new \MongoTimestamp(1234567890);
$timestamp2 = new \MongoTimestamp(1234567890);
- $this->assertAttributeSame(0, 'inc', $timestamp1);
- $this->assertAttributeSame(1, 'inc', $timestamp2);
+ $this->assertSame(0, $timestamp1->inc);
+ $this->assertSame(1, $timestamp2->inc);
}
public function testCreateWithBsonTimestamp()
@@ -49,8 +49,8 @@ public function testCreateWithBsonTimestamp()
$bsonTimestamp = new \MongoDB\BSON\Timestamp(987654321, 1234567890);
$timestamp = new \MongoTimestamp($bsonTimestamp);
- $this->assertAttributeSame(1234567890, 'sec', $timestamp);
- $this->assertAttributeSame(987654321, 'inc', $timestamp);
+ $this->assertSame(1234567890, $timestamp->sec);
+ $this->assertSame(987654321, $timestamp->inc);
}
public function testContructorArgumentOrderDiffers()
@@ -63,6 +63,6 @@ public function testContructorArgumentOrderDiffers()
$bsonTimestamp = new \MongoDB\BSON\Timestamp(12345, 67890);
$timestamp = new \MongoTimestamp(67890, 12345);
- $this->assertSame((string) $bsonTimestamp, (string)$timestamp->toBSONType());
+ $this->assertSame((string) $bsonTimestamp, (string) $timestamp->toBSONType());
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoUpdateBatchTest.php b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoUpdateBatchTest.php
index 89458dfa..858fe8c7 100644
--- a/tests/Alcaeus/MongoDbAdapter/Mongo/MongoUpdateBatchTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/Mongo/MongoUpdateBatchTest.php
@@ -9,7 +9,7 @@ class MongoUpdateBatchTest extends TestCase
public function testSerialize()
{
$batch = new \MongoUpdateBatch($this->getCollection());
- $this->assertInternalType('string', serialize($batch));
+ $this->assertIsString(serialize($batch));
}
public function testUpdateOne()
@@ -35,8 +35,7 @@ public function testUpdateOne()
$this->assertSame(1, $newCollection->count());
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('foo', 'foo', $record);
+ $this->assertSame('foo', $record->foo);
}
public function testUpdateOneException()
@@ -71,7 +70,7 @@ public function testUpdateOneException()
} catch (\MongoWriteConcernException $e) {
$this->assertSame('Failed write', $e->getMessage());
$this->assertSame(911, $e->getCode());
- $this->assertArraySubset($expected, $e->getDocument());
+ $this->assertMatches($expected, $e->getDocument());
}
}
@@ -100,8 +99,35 @@ public function testUpdateMany()
$this->assertSame(2, $newCollection->count());
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('foo', 'foo', $record);
+ $this->assertSame('foo', $record->foo);
+ }
+
+ public function testUpdateManyWithoutAck()
+ {
+ $collection = $this->getCollection();
+ $batch = new \MongoUpdateBatch($collection);
+
+ $document = ['foo' => 'bar'];
+ $collection->insert($document);
+ unset($document['_id']);
+ $collection->insert($document);
+
+ $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'u' => ['$set' => ['foo' => 'foo']], 'multi' => true]));
+
+ $expected = [
+ 'nMatched' => 0,
+ 'nModified' => 0,
+ 'nUpserted' => 0,
+ 'ok' => true,
+ ];
+
+ $this->assertSame($expected, $batch->execute(['w' => 0]));
+
+ $newCollection = $this->getCheckDatabase()->selectCollection('test');
+ $this->assertSame(2, $newCollection->count());
+ $record = $newCollection->findOne();
+ $this->assertNotNull($record);
+ $this->assertSame('foo', $record->foo);
}
public function testUpdateManyException()
@@ -136,7 +162,7 @@ public function testUpdateManyException()
} catch (\MongoWriteConcernException $e) {
$this->assertSame('Failed write', $e->getMessage());
$this->assertSame(911, $e->getCode());
- $this->assertArraySubset($expected, $e->getDocument());
+ $this->assertMatches($expected, $e->getDocument());
}
}
@@ -162,7 +188,7 @@ public function testUpsert()
];
$result = $batch->execute();
- $this->assertArraySubset($expected, $result);
+ $this->assertMatches($expected, $result);
$this->assertInstanceOf('MongoId', $result['upserted'][0]['_id']);
@@ -171,8 +197,7 @@ public function testUpsert()
$this->assertSame(2, $newCollection->count());
$record = $newCollection->findOne();
$this->assertNotNull($record);
- $this->assertObjectHasAttribute('foo', $record);
- $this->assertAttributeSame('bar', 'foo', $record);
+ $this->assertSame('bar', $record->foo);
}
public function testValidateItem()
diff --git a/tests/Alcaeus/MongoDbAdapter/TestCase.php b/tests/Alcaeus/MongoDbAdapter/TestCase.php
index 153ad167..1250d1e0 100644
--- a/tests/Alcaeus/MongoDbAdapter/TestCase.php
+++ b/tests/Alcaeus/MongoDbAdapter/TestCase.php
@@ -2,17 +2,29 @@
namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\Tests\Constraint\Matches;
use MongoDB\Client;
use PHPUnit\Framework\TestCase as BaseTestCase;
+use Symfony\Bridge\PhpUnit\SetUpTearDownTrait;
abstract class TestCase extends BaseTestCase
{
+ use SetUpTearDownTrait;
+
const INDEX_VERSION_1 = 1;
const INDEX_VERSION_2 = 2;
- protected function tearDown()
+ private function doTearDown()
{
$this->getCheckDatabase()->drop();
+
+ parent::tearDown();
+ }
+
+ public function assertMatches($expected, $value, $message = '')
+ {
+ $constraint = new Matches($expected, true, true, true);
+ $this->assertThat($value, $constraint, $message);
}
/**
@@ -20,7 +32,7 @@ protected function tearDown()
*/
protected function getCheckClient()
{
- return new Client('mongodb://localhost', ['connect' => true]);
+ return new Client(MONGODB_URI, ['connect' => true]);
}
/**
@@ -36,7 +48,7 @@ protected function getCheckDatabase()
* @param array|null $options
* @return \MongoClient
*/
- protected function getClient($options = null, $uri = 'mongodb://localhost')
+ protected function getClient($options = null, $uri = MONGODB_URI)
{
$args = [$uri];
if ($options !== null) {
@@ -139,7 +151,7 @@ protected function checkFailPoint()
/* command not found */
if ($e->getCode() == 59) {
$this->markTestSkipped(
- 'This test require the mongo daemon to be started with the test flag: --setParameter enableTestCommands=1'
+ 'This test require the mongo daemon to be started with the test flag: --setParameter enableTestCommands=1'
);
}
}
@@ -153,18 +165,19 @@ protected function failMaxTimeMS()
/**
* @param bool $condition
*/
- protected function skipTestUnless($condition)
+ protected function skipTestUnless($condition, $message = null)
{
- $this->skipTestIf(! $condition);
+ $this->skipTestIf(! $condition, $message);
}
/**
* @param bool $condition
+ * @param string|null $message
*/
- protected function skipTestIf($condition)
+ protected function skipTestIf($condition, $message = null)
{
if ($condition) {
- $this->markTestSkipped('Test only applies when running against mongo-php-adapter');
+ $this->markTestSkipped($message !== null ? $message : 'Test only applies when running against mongo-php-adapter');
}
}
@@ -183,7 +196,11 @@ protected function getServerVersion()
protected function getFeatureCompatibilityVersion()
{
$featureCompatibilityVersion = $this->getClient()->selectDB('admin')->command(['getParameter' => true, 'featureCompatibilityVersion' => true]);
- return isset($featureCompatibilityVersion['featureCompatibilityVersion']) ? $featureCompatibilityVersion['featureCompatibilityVersion'] : '3.2';
+ if (! isset($featureCompatibilityVersion['featureCompatibilityVersion'])) {
+ return '3.2';
+ }
+
+ return isset($featureCompatibilityVersion['featureCompatibilityVersion']['version']) ? $featureCompatibilityVersion['featureCompatibilityVersion']['version'] : $featureCompatibilityVersion['featureCompatibilityVersion'];
}
/**
@@ -199,6 +216,6 @@ protected function getDefaultIndexVersion()
// Check featureCompatibilityFlag
$compatibilityVersion = $this->getFeatureCompatibilityVersion();
- return $compatibilityVersion === '3.4' ? self::INDEX_VERSION_2 : self::INDEX_VERSION_1;
+ return version_compare($compatibilityVersion, '3.4', '>=') ? self::INDEX_VERSION_2 : self::INDEX_VERSION_1;
}
}
diff --git a/tests/Alcaeus/MongoDbAdapter/TypeConverterTest.php b/tests/Alcaeus/MongoDbAdapter/TypeConverterTest.php
index 635c0c25..7bab4a7c 100644
--- a/tests/Alcaeus/MongoDbAdapter/TypeConverterTest.php
+++ b/tests/Alcaeus/MongoDbAdapter/TypeConverterTest.php
@@ -3,7 +3,6 @@
namespace Alcaeus\MongoDbAdapter\Tests;
use MongoDB\BSON;
-use MongoDB\Driver\Exception;
use Alcaeus\MongoDbAdapter\TypeConverter;
use MongoDB\Model\BSONDocument;
@@ -38,6 +37,14 @@ public static function converterData()
'nestedArrays' => [
[['foo' => 'bar']], [new BSONDocument(['foo' => 'bar'])]
],
+ 'dateTime' => [
+ \DateTime::createFromFormat('Y-m-d\TH:i:sP', '2021-06-30T12:34:56-7'),
+ new BSONDocument([
+ 'date' => '2021-06-30 12:34:56.000000',
+ 'timezone_type' => 1,
+ 'timezone' => '-07:00',
+ ]),
+ ],
];
}