diff --git a/.github/workflows/build-ci-atlas.yml b/.github/workflows/build-ci-atlas.yml index 58b05907c..f8a9ac1c3 100644 --- a/.github/workflows/build-ci-atlas.yml +++ b/.github/workflows/build-ci-atlas.yml @@ -1,13 +1,13 @@ -name: "Atlas CI" +name: Atlas CI on: push: branches: - - "[0-9]+.[0-9x]+" + - '[0-9]+.[0-9x]+' pull_request: branches: - - "[0-9]+.[0-9x]+" - - "feature/*" + - '[0-9]+.[0-9x]+' + - feature/* env: MONGODB_EXT_V1: mongodb-1.21.0 @@ -15,39 +15,33 @@ env: jobs: build: - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest - name: "PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }}" + name: PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }} strategy: matrix: - php: - - "8.2" - - "8.3" - - "8.4" - - "8.5" - laravel: - - "12.*" - driver: - - 2 + php: ['8.2', '8.3', '8.4', '8.5'] + laravel: ['12.*', '13.*'] + driver: [2] include: - # Test Laravel 10 - - php: "8.1" - laravel: "10.*" + - php: '8.1' + laravel: 10.* driver: 1 - # Test Laravel 11 - - php: "8.2" - laravel: "11.*" + - php: '8.2' + laravel: 11.* driver: 1 - # Test Driver v1 - - php: "8.4" - laravel: "12.*" + - php: '8.4' + laravel: 12.* driver: 1 + exclude: + - laravel: 13.* + php: '8.2' steps: - - uses: "actions/checkout@v6" + - uses: actions/checkout@v6 - - name: "Create MongoDB Atlas Local" + - name: Create MongoDB Atlas Local run: | docker run --name mongodb -p 27017:27017 --detach mongodb/mongodb-atlas-local:latest until docker exec --tty mongodb mongosh --eval "db.runCommand({ ping: 1 })"; do @@ -57,7 +51,7 @@ jobs: sleep 1 done - - name: "Show MongoDB server status" + - name: Show MongoDB server status run: | docker exec --tty mongodb mongosh --eval "db.runCommand({ serverStatus: 1 })" @@ -67,39 +61,39 @@ jobs: with: php-version: ${{ matrix.php }} extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} - key: "extcache-v1" + key: extcache-v1 - - name: "Installing php" - uses: "shivammathur/setup-php@v2" + - name: Installing php + uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: "curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" - coverage: "xdebug" - tools: "composer" + extensions: curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} + coverage: xdebug + tools: composer - - name: "Show Docker version" + - name: Show Docker version if: ${{ runner.debug }} - run: "docker version && env" + run: docker version && env - - name: "Restrict Laravel version" - run: "composer require --dev --no-update 'laravel/framework:${{ matrix.laravel }}'" + - name: Restrict Laravel version + run: composer require --dev --no-update 'laravel/framework:${{ matrix.laravel }}' - - name: "Download Composer cache dependencies from cache" - id: "composer-cache" + - name: Download Composer cache dependencies from cache + id: composer-cache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - name: "Cache Composer dependencies" - uses: "actions/cache@v5" + - name: Cache Composer dependencies + uses: actions/cache@v5 with: path: ${{ steps.composer-cache.outputs.dir }} - key: "${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}" - restore-keys: "${{ matrix.os }}-composer-" + key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ matrix.os }}-composer- - - name: "Install dependencies" + - name: Install dependencies run: | composer update --no-interaction - - name: "Run tests" + - name: Run tests run: | export MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" php -d zend.assertions=1 ./vendor/bin/phpunit --coverage-clover coverage.xml --group atlas-search diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 3bd66d839..8aa8e96da 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -1,13 +1,13 @@ -name: "CI" +name: CI on: push: branches: - - "[0-9]+.[0-9x]+" + - '[0-9]+.[0-9x]+' pull_request: branches: - - "[0-9]+.[0-9x]+" - - "feature/*" + - '[0-9]+.[0-9x]+' + - feature/* env: MONGODB_EXT_V1: mongodb-1.21.0 @@ -15,68 +15,59 @@ env: jobs: build: - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest - name: "PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }} Server/${{ matrix.mongodb }} ${{ matrix.mode }}" + name: PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }} Server/${{ matrix.mongodb }} ${{ matrix.mode }} strategy: matrix: - mongodb: - - "8.0" - php: - - "8.2" - - "8.3" - - "8.4" - - "8.5" - laravel: - - "12.*" - driver: - - 2 + mongodb: ['8.0'] + php: ['8.2', '8.3', '8.4', '8.5'] + laravel: ['12.*', '13.*'] + driver: [2] include: - # Test Lowest dependencies - - php: "8.1" - laravel: "10.*" - mongodb: "5.0" - mode: "low-deps" + - php: '8.1' + laravel: 10.* + mongodb: '5.0' + mode: low-deps driver: 1 - # Test Driver v1 - - php: "8.3" - laravel: "10.*" - mongodb: "4.4" + - php: '8.3' + laravel: 10.* + mongodb: '4.4' driver: 1 - - php: "8.4" - laravel: "12.*" - mongodb: "8.0" + - php: '8.4' + laravel: 12.* + mongodb: '8.0' driver: 1 - # Test Laravel 10 - - php: "8.5" - laravel: "10.*" - mongodb: "8.0" - # Test Laravel 11 - - php: "8.5" - laravel: "11.*" - mongodb: "8.0" - # Test all server versions with the lowest PHP and latest Laravel - - php: "8.2" - laravel: "12.*" - mongodb: "4.4" - - php: "8.2" - laravel: "12.*" - mongodb: "5.0" - - php: "8.2" - laravel: "12.*" - mongodb: "6.0" - - php: "8.2" - laravel: "12.*" - mongodb: "7.0" - - php: "8.2" - laravel: "12.*" - mongodb: "8.0" + - php: '8.5' + laravel: 10.* + mongodb: '8.0' + - php: '8.5' + laravel: 11.* + mongodb: '8.0' + - php: '8.2' + laravel: 12.* + mongodb: '4.4' + - php: '8.2' + laravel: 12.* + mongodb: '5.0' + - php: '8.2' + laravel: 12.* + mongodb: '6.0' + - php: '8.2' + laravel: 12.* + mongodb: '7.0' + - php: '8.2' + laravel: 12.* + mongodb: '8.0' + exclude: + - laravel: 13.* + php: '8.2' steps: - - uses: "actions/checkout@v6" + - uses: actions/checkout@v6 - - name: "Create MongoDB Replica Set" + - name: Create MongoDB Replica Set run: | docker run --name mongodb -p 27017:27017 -e MONGO_INITDB_DATABASE=unittest --detach mongo:${{ matrix.mongodb }} mongod --replSet rs --setParameter transactionLifetimeLimitSeconds=5 @@ -86,7 +77,7 @@ jobs: done sudo docker exec --tty mongodb $MONGOSH_BIN --eval "rs.initiate({\"_id\":\"rs\",\"members\":[{\"_id\":0,\"host\":\"127.0.0.1:27017\" }]})" - - name: "Show MongoDB server status" + - name: Show MongoDB server status run: | if [ "${{ matrix.mongodb }}" = "4.4" ]; then MONGOSH_BIN="mongo"; else MONGOSH_BIN="mongosh"; fi docker exec --tty mongodb $MONGOSH_BIN --eval "db.runCommand({ serverStatus: 1 })" @@ -97,40 +88,41 @@ jobs: with: php-version: ${{ matrix.php }} extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} - key: "extcache-v1" + key: extcache-v1 - - name: "Installing php" - uses: "shivammathur/setup-php@v2" + - name: Installing php + uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: "curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" - coverage: "xdebug" - tools: "composer" + extensions: curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} + coverage: xdebug + tools: composer - - name: "Show Docker version" + - name: Show Docker version if: ${{ runner.debug }} - run: "docker version && env" + run: docker version && env - - name: "Restrict Laravel version" - run: "composer require --dev --no-update 'laravel/framework:${{ matrix.laravel }}'" + - name: Restrict Laravel version + run: composer require --dev --no-update 'laravel/framework:${{ matrix.laravel }}' - - name: "Download Composer cache dependencies from cache" - id: "composer-cache" + - name: Download Composer cache dependencies from cache + id: composer-cache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - name: "Cache Composer dependencies" - uses: "actions/cache@v5" + - name: Cache Composer dependencies + uses: actions/cache@v5 with: path: ${{ steps.composer-cache.outputs.dir }} - key: "${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}" - restore-keys: "${{ matrix.os }}-composer-" + key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ matrix.os }}-composer- - - name: "Install dependencies" + - name: Install dependencies run: | composer update --no-interaction \ $([[ "${{ matrix.mode }}" == low-deps ]] && echo ' --prefer-lowest') \ $([[ "${{ matrix.mode }}" == ignore-php-req ]] && echo ' --ignore-platform-req=php+') - - name: "Run tests" + + - name: Run tests run: | export MONGODB_URI="mongodb://127.0.0.1:27017/?replicaSet=rs" - php -d zend.assertions=1 ./vendor/bin/phpunit --coverage-clover coverage.xml --exclude-group atlas-search + php -d zend.assertions=1 ./vendor/bin/phpunit --coverage-clover coverage.xml --exclude-group atlas-search --testdox diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index b107a199d..c34c37833 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -1,51 +1,49 @@ -name: "Static Analysis" +name: Static Analysis on: push: branches: - - "[0-9]+.[0-9x]+" + - [0-9]+.[0-9x]+ pull_request: branches: - - "[0-9]+.[0-9x]+" - - "feature/*" + - [0-9]+.[0-9x]+ + - feature/* workflow_call: inputs: ref: - description: "The git ref to check" + description: The git ref to check type: string required: true env: - PHP_VERSION: "8.5" - DRIVER_VERSION: "stable" + PHP_VERSION: '8.5' + DRIVER_VERSION: stable MONGODB_EXT_V1: mongodb-1.21.0 MONGODB_EXT_V2: mongodb-mongodb/mongo-php-driver@v2.x jobs: phpstan: - name: "PHP/${{ matrix.php }} Driver/${{ matrix.driver }}" - runs-on: "ubuntu-22.04" + name: PHP/${{ matrix.php }} Driver/${{ matrix.driver }} + + runs-on: ubuntu-22.04 + continue-on-error: true + strategy: matrix: - php: - - '8.1' - - '8.2' - - '8.3' - - '8.4' - - '8.5' - driver: - - 2 + php: ['8.1', '8.2', '8.3', '8.4', '8.5'] + driver: [2] include: - - php: "8.4" + - php: '8.4' driver: 1 + steps: - name: Checkout uses: actions/checkout@v6 with: ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }} - - name: "Get SHA hash of checked out ref" + - name: Get SHA hash of checked out ref if: ${{ github.event_name == 'workflow_dispatch' }} run: | echo CHECKED_OUT_SHA=$(git rev-parse HEAD) >> $GITHUB_ENV @@ -56,13 +54,13 @@ jobs: with: php-version: ${{ matrix.php }} extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} - key: "extcache-v1" + key: extcache-v1 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: "curl,mbstring,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" + extensions: curl,mbstring,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} tools: composer:v2 coverage: none @@ -81,7 +79,7 @@ jobs: uses: actions/cache/restore@v5 with: path: .cache - key: "phpstan-result-cache-${{ matrix.php }}-${{ github.run_id }}" + key: phpstan-result-cache-${{ matrix.php }}-${{ github.run_id }} restore-keys: | phpstan-result-cache- @@ -89,15 +87,15 @@ jobs: run: ./vendor/bin/phpstan analyse --no-interaction --no-progress --ansi --error-format=sarif > phpstan.sarif continue-on-error: true - - name: "Upload SARIF report" + - name: Upload SARIF report if: ${{ github.event_name != 'workflow_dispatch' }} - uses: "github/codeql-action/upload-sarif@v4" + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: phpstan.sarif - - name: "Upload SARIF report" + - name: Upload SARIF report if: ${{ github.event_name == 'workflow_dispatch' }} - uses: "github/codeql-action/upload-sarif@v4" + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: phpstan.sarif ref: ${{ inputs.ref }} diff --git a/composer.json b/composer.json index 658e7f112..169b91978 100644 --- a/composer.json +++ b/composer.json @@ -15,35 +15,55 @@ "security": "https://www.mongodb.com/security" }, "authors": [ - { "name": "Andreas Braun", "email": "andreas.braun@mongodb.com", "role": "Leader" }, - { "name": "Pauline Vos", "email": "pauline.vos@mongodb.com", "role": "Maintainer" }, - { "name": "Jérôme Tamarelle", "email": "jerome.tamarelle@mongodb.com", "role": "Maintainer" }, - { "name": "Jeremy Mikola", "email": "jmikola@gmail.com", "role": "Maintainer" }, - { "name": "Jens Segers", "homepage": "https://jenssegers.com", "role": "Creator" } + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com", + "role": "Leader" + }, + { + "name": "Pauline Vos", + "email": "pauline.vos@mongodb.com", + "role": "Maintainer" + }, + { + "name": "Jérôme Tamarelle", + "email": "jerome.tamarelle@mongodb.com", + "role": "Maintainer" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com", + "role": "Maintainer" + }, + { + "name": "Jens Segers", + "homepage": "https://jenssegers.com", + "role": "Creator" + } ], "license": "MIT", "require": { "php": "^8.1", "ext-mongodb": "^1.21|^2", "composer-runtime-api": "^2.0.0", - "illuminate/cache": "^10.36|^11|^12", - "illuminate/container": "^10.0|^11|^12", - "illuminate/database": "^10.30|^11|^12", - "illuminate/events": "^10.0|^11|^12", - "illuminate/support": "^10.0|^11|^12", + "illuminate/cache": "^10.36|^11|^12|^13.0", + "illuminate/container": "^10.0|^11|^12|^13.0", + "illuminate/database": "^10.30|^11|^12|^13.0", + "illuminate/events": "^10.0|^11|^12|^13.0", + "illuminate/support": "^10.0|^11|^12|^13.0", "mongodb/mongodb": "^1.21|^2" }, "require-dev": { "laravel/scout": "^10.3", "league/flysystem-gridfs": "^3.28", "league/flysystem-read-only": "^3.0", - "phpunit/phpunit": "^10.3|^11.5.3", - "orchestra/testbench": "^8.0|^9.0|^10.0", + "phpunit/phpunit": "^10.3|^11.5.3|^12.5.12", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", "mockery/mockery": "^1.4.4", - "doctrine/coding-standard": "12.0.x-dev", + "doctrine/coding-standard": "^12.0", "spatie/laravel-query-builder": "^5.6|^6", - "phpstan/phpstan": "^1.10", - "rector/rector": "^1.2" + "phpstan/phpstan": "^1.10|^2.1", + "rector/rector": "^1.2|^2.3" }, "conflict": { "illuminate/bus": "< 10.37.2" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e31acd401..1c90fc112 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,7 +18,6 @@ - diff --git a/src/Cache/MongoStore.php b/src/Cache/MongoStore.php index 0b800bcf3..cf959ec2c 100644 --- a/src/Cache/MongoStore.php +++ b/src/Cache/MongoStore.php @@ -258,6 +258,27 @@ public function forgetIfExpired($key): bool return $result->getDeletedCount() > 0; } + /** + * Extend the expiration time of an item in the cache. + * + * @param string $key + * @param int $seconds + */ + public function touch($key, $seconds): bool + { + $result = $this->collection->updateOne( + [ + '_id' => $this->prefix . $key, + 'expires_at' => ['$gt' => $this->getUTCDateTime()], + ], + [ + '$set' => ['expires_at' => $this->getUTCDateTime($seconds)], + ], + ); + + return $result->getModifiedCount() > 0; + } + public function flush(): bool { $this->collection->deleteMany([]); diff --git a/src/MongoDBServiceProvider.php b/src/MongoDBServiceProvider.php index 644eb7a56..34502cd1a 100644 --- a/src/MongoDBServiceProvider.php +++ b/src/MongoDBServiceProvider.php @@ -112,7 +112,7 @@ private function registerFlysystemAdapter(): void { // GridFS adapter for filesystem $this->app->resolving('filesystem', static function (FilesystemManager $filesystemManager) { - $filesystemManager->extend('gridfs', static function (Application $app, array $config) { + $filesystemManager->extend('gridfs', function (Application $app, array $config) { if (! class_exists(GridFSAdapter::class)) { throw new RuntimeException('GridFS adapter for Flysystem is missing. Try running "composer require league/flysystem-gridfs"'); } diff --git a/tests/Cache/MongoCacheStoreTest.php b/tests/Cache/MongoCacheStoreTest.php index 6f4ee79f4..c6a0658a6 100644 --- a/tests/Cache/MongoCacheStoreTest.php +++ b/tests/Cache/MongoCacheStoreTest.php @@ -201,6 +201,43 @@ public function testIncrementDecrement() $this->assertFalse($store->increment('foo', 5)); } + public function testTouchReturnsFalseWhenKeyDoesNotExist() + { + $store = $this->getStore(); + + $this->assertFalse($store->touch('foo', 60)); + } + + public function testTouchExtendsExpirationAndPreservesValue() + { + $store = $this->getStore(); + + $this->insertToCacheTable('foo', 'bar', 60); + $result = $store->touch('foo', 3600); + + $this->assertTrue($result); + $this->assertSame('bar', $store->get('foo')); + + $document = DB::connection('mongodb') + ->getCollection($this->getCacheCollectionName()) + ->findOne(['_id' => $this->withCachePrefix('foo')]); + + $this->assertGreaterThan( + new UTCDateTime(Carbon::now()->addSeconds(60)), + $document['expires_at'], + ); + } + + public function testTouchReturnsFalseOnExpiredItem() + { + $store = $this->getStore(); + + $this->insertToCacheTable('foo', 'bar', -5); + + $this->assertFalse($store->touch('foo', 60)); + $this->assertNull($store->get('foo')); + } + public function testTTLIndex() { $store = $this->getStore(); diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 06de1ce3a..bdc71d401 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -1317,9 +1317,10 @@ public function testUpdateOrCreate(array $criteria) #[TestWith(['id'])] public function testCreateWithNullId(string $id) { + User::truncate(); $user = User::create([$id => null, 'email' => 'foo@bar']); - $this->assertNotNull(ObjectId::class, $user->id); - $this->assertSame(1, User::count()); + $this->assertIsString($user->id); + $this->assertEquals([['id' => $user->id, 'email' => 'foo@bar']], User::all(['id', 'email'])->toArray()); } /** @param class-string $modelClass */