diff --git a/admin/tool/policy/tests/api_test.php b/admin/tool/policy/tests/api_test.php
index de473299f63..0b428fd3c2b 100644
--- a/admin/tool/policy/tests/api_test.php
+++ b/admin/tool/policy/tests/api_test.php
@@ -37,7 +37,7 @@ class api_test extends \advanced_testcase {
 
         // Prepare the form data for adding a new policy document.
         $formdata = api::form_policydoc_data(new policy_version(0));
-        $this->assertObjectHasAttribute('name', $formdata);
+        $this->assertObjectHasProperty('name', $formdata);
         $this->assertArrayHasKey('text', $formdata->summary_editor);
         $this->assertArrayHasKey('format', $formdata->content_editor);
 
diff --git a/admin/tool/uploaduser/tests/cli_test.php b/admin/tool/uploaduser/tests/cli_test.php
index f053eec7dea..bea9eab178b 100644
--- a/admin/tool/uploaduser/tests/cli_test.php
+++ b/admin/tool/uploaduser/tests/cli_test.php
@@ -143,7 +143,7 @@ class cli_test extends \advanced_testcase {
         // Created users have data in the profile fields.
         $user1 = \core_user::get_user_by_username('reznort');
         $profilefields1 = profile_user_record($user1->id);
-        $this->assertObjectHasAttribute('superfield', $profilefields1);
+        $this->assertObjectHasProperty('superfield', $profilefields1);
         $this->assertEquals('Loves cats', $profilefields1->superfield);
     }
 
diff --git a/auth/tests/privacy/provider_test.php b/auth/tests/privacy/provider_test.php
index 6e041552087..469acfe6ba9 100644
--- a/auth/tests/privacy/provider_test.php
+++ b/auth/tests/privacy/provider_test.php
@@ -94,11 +94,11 @@ class provider_test extends provider_testcase {
         $prefs = writer::with_context($sysctx)->get_user_preferences('core_auth');
         $this->assertEquals(transform::yesno(false), $prefs->auth_forcepasswordchange->value);
         $this->assertEquals(transform::yesno(false), $prefs->create_password->value);
-        $this->assertObjectNotHasAttribute('login_failed_count', $prefs);
-        $this->assertObjectNotHasAttribute('login_failed_count_since_success', $prefs);
-        $this->assertObjectNotHasAttribute('login_failed_last', $prefs);
-        $this->assertObjectNotHasAttribute('login_lockout', $prefs);
+        $this->assertObjectNotHasProperty('login_failed_count', $prefs);
+        $this->assertObjectNotHasProperty('login_failed_count_since_success', $prefs);
+        $this->assertObjectNotHasProperty('login_failed_last', $prefs);
+        $this->assertObjectNotHasProperty('login_lockout', $prefs);
         $this->assertEquals(transform::yesno(true), $prefs->login_lockout_ignored->value);
-        $this->assertObjectNotHasAttribute('login_lockout_secret', $prefs);
+        $this->assertObjectNotHasProperty('login_lockout_secret', $prefs);
     }
 }
diff --git a/badges/tests/badgeslib_test.php b/badges/tests/badgeslib_test.php
index 555e2d8a704..a5268d839da 100644
--- a/badges/tests/badgeslib_test.php
+++ b/badges/tests/badgeslib_test.php
@@ -330,8 +330,8 @@ class badgeslib_test extends advanced_testcase {
         $message = array_pop($messages);
         // Check we have the expected data.
         $customdata = json_decode($message->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
-        $this->assertObjectHasAttribute('hash', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('hash', $customdata);
 
         $user2 = $this->getDataGenerator()->create_user();
         $badge->issue($user2->id, true);
diff --git a/blocks/tests/privacy/provider_test.php b/blocks/tests/privacy/provider_test.php
index eb6bcf1618e..6438ccc081c 100644
--- a/blocks/tests/privacy/provider_test.php
+++ b/blocks/tests/privacy/provider_test.php
@@ -381,12 +381,12 @@ class provider_test extends provider_testcase {
         $this->assertEquals($yes, $prefs->block_is_hidden->value);
 
         $prefs = writer::with_context($bprivatefiles->context)->get_user_context_preferences('core_block');
-        $this->assertObjectNotHasAttribute('block_is_docked', $prefs);
+        $this->assertObjectNotHasProperty('block_is_docked', $prefs);
         $this->assertEquals($no, $prefs->block_is_hidden->value);
 
         $prefs = writer::with_context($bmyprofile->context)->get_user_context_preferences('core_block');
         $this->assertEquals($yes, $prefs->block_is_docked->value);
-        $this->assertObjectNotHasAttribute('block_is_hidden', $prefs);
+        $this->assertObjectNotHasProperty('block_is_hidden', $prefs);
     }
 
     /**
diff --git a/cohort/tests/lib_test.php b/cohort/tests/lib_test.php
index 8bf48250d1c..639a48be4bd 100644
--- a/cohort/tests/lib_test.php
+++ b/cohort/tests/lib_test.php
@@ -711,7 +711,7 @@ class lib_test extends \advanced_testcase {
 
         // Test cohort_get_cohort.
         $result = cohort_get_cohort($cohort1->id, $coursectx, true);
-        $this->assertObjectHasAttribute('customfields', $result);
+        $this->assertObjectHasProperty('customfields', $result);
         $this->assertCount(1, $result->customfields);
         $field = reset($result->customfields);
         $this->assertInstanceOf(data_controller::class, $field);
@@ -720,14 +720,14 @@ class lib_test extends \advanced_testcase {
 
         // Test custom fields are not returned if not needed.
         $result = cohort_get_cohort($cohort1->id, $coursectx);
-        $this->assertObjectNotHasAttribute('customfields', $result);
+        $this->assertObjectNotHasProperty('customfields', $result);
 
         // Test cohort_get_cohorts.
         $result = cohort_get_cohorts(\context_system::instance()->id, 0, 25, '', true);
         $this->assertEquals(2, $result['totalcohorts']);
         $this->assertEquals(2, $result['allcohorts']);
         foreach ($result['cohorts'] as $cohort) {
-            $this->assertObjectHasAttribute('customfields', $cohort);
+            $this->assertObjectHasProperty('customfields', $cohort);
             $this->assertCount(1, $cohort->customfields);
             $field = reset($cohort->customfields);
             $this->assertInstanceOf(data_controller::class, $field);
@@ -745,7 +745,7 @@ class lib_test extends \advanced_testcase {
         $this->assertEquals(2, $result['totalcohorts']);
         $this->assertEquals(2, $result['allcohorts']);
         foreach ($result['cohorts'] as $cohort) {
-            $this->assertObjectNotHasAttribute('customfields', $cohort);
+            $this->assertObjectNotHasProperty('customfields', $cohort);
         }
 
         // Test test_cohort_get_all_cohorts.
@@ -753,7 +753,7 @@ class lib_test extends \advanced_testcase {
         $this->assertEquals(2, $result['totalcohorts']);
         $this->assertEquals(2, $result['allcohorts']);
         foreach ($result['cohorts'] as $cohort) {
-            $this->assertObjectHasAttribute('customfields', $cohort);
+            $this->assertObjectHasProperty('customfields', $cohort);
             $this->assertCount(1, $cohort->customfields);
             $field = reset($cohort->customfields);
             $this->assertInstanceOf(data_controller::class, $field);
@@ -771,14 +771,14 @@ class lib_test extends \advanced_testcase {
         $this->assertEquals(2, $result['totalcohorts']);
         $this->assertEquals(2, $result['allcohorts']);
         foreach ($result['cohorts'] as $cohort) {
-            $this->assertObjectNotHasAttribute('customfields', $cohort);
+            $this->assertObjectNotHasProperty('customfields', $cohort);
         }
 
         // Test cohort_get_available_cohorts.
         $result = cohort_get_available_cohorts($coursectx, COHORT_ALL, 0, 25, '', true);
         $this->assertCount(2, $result);
         foreach ($result as $cohort) {
-            $this->assertObjectHasAttribute('customfields', $cohort);
+            $this->assertObjectHasProperty('customfields', $cohort);
             $this->assertCount(1, $cohort->customfields);
             $field = reset($cohort->customfields);
             $this->assertInstanceOf(data_controller::class, $field);
@@ -795,7 +795,7 @@ class lib_test extends \advanced_testcase {
         $result = cohort_get_available_cohorts($coursectx, COHORT_ALL, 0, 25, '');
         $this->assertCount(2, $result);
         foreach ($result as $cohort) {
-            $this->assertObjectNotHasAttribute('customfields', $cohort);
+            $this->assertObjectNotHasProperty('customfields', $cohort);
         }
 
         // Test cohort_get_user_cohorts.
@@ -805,7 +805,7 @@ class lib_test extends \advanced_testcase {
         $result = cohort_get_user_cohorts($user->id, true);
         $this->assertCount(2, $result);
         foreach ($result as $cohort) {
-            $this->assertObjectHasAttribute('customfields', $cohort);
+            $this->assertObjectHasProperty('customfields', $cohort);
             $this->assertCount(1, $cohort->customfields);
             $field = reset($cohort->customfields);
             $this->assertInstanceOf(data_controller::class, $field);
@@ -822,7 +822,7 @@ class lib_test extends \advanced_testcase {
         $result = cohort_get_user_cohorts($user->id);
         $this->assertCount(2, $result);
         foreach ($result as $cohort) {
-            $this->assertObjectNotHasAttribute('customfields', $cohort);
+            $this->assertObjectNotHasProperty('customfields', $cohort);
         }
     }
 
diff --git a/communication/provider/matrix/tests/matrix_user_manager_test.php b/communication/provider/matrix/tests/matrix_user_manager_test.php
index 860fb10cf5e..00877c4b9ba 100644
--- a/communication/provider/matrix/tests/matrix_user_manager_test.php
+++ b/communication/provider/matrix/tests/matrix_user_manager_test.php
@@ -229,6 +229,6 @@ class matrix_user_manager_test extends \advanced_testcase {
         $this->assertNotFalse($matrixprofilefield);
 
         $user = $this->getDataGenerator()->create_user();
-        $this->assertObjectHasAttribute($matrixprofilefield, profile_user_record($user->id));
+        $this->assertObjectHasProperty($matrixprofilefield, profile_user_record($user->id));
     }
 }
diff --git a/competency/tests/lib_test.php b/competency/tests/lib_test.php
index 660d4c4dfae..2d29a0c4134 100644
--- a/competency/tests/lib_test.php
+++ b/competency/tests/lib_test.php
@@ -99,7 +99,7 @@ class lib_test extends \advanced_testcase {
         $this->assertEquals($expectedurlname, $message->contexturlname);
         // Test customdata.
         $customdata = json_decode($message->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
         $this->assertStringContainsString('tokenpluginfile.php', $customdata->notificationiconurl);
         $userpicture = new \user_picture($u1);
         $userpicture->size = 1; // Use f1 size.
@@ -229,7 +229,7 @@ class lib_test extends \advanced_testcase {
         $this->assertEquals($u1->id, $message->useridto);
         // Test customdata.
         $customdata = json_decode($message->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
 
         // Post a comment in a plan with reviewer. The reviewer is messaged.
         $p1->set('reviewerid', $u2->id);
diff --git a/composer.json b/composer.json
index f6115aed593..bfeeb39b835 100644
--- a/composer.json
+++ b/composer.json
@@ -5,15 +5,15 @@
     "type": "project",
     "homepage": "https://moodle.org",
     "require-dev": {
-        "phpunit/phpunit": "9.5.*",
+        "phpunit/phpunit": "^9.6.18",
         "mikey179/vfsstream": "1.6.*",
-        "behat/mink": "^1.10.0",
-        "friends-of-behat/mink-extension": "^2.7.4",
-        "behat/mink-browserkit-driver": "^2.1.0",
-        "symfony/process": "^4.4 || ^5.0 || ^6.0",
-        "symfony/http-client": "^4.4 || ^5.0 || ^6.0",
-        "symfony/mime": "^4.4 || ^5.0 || ^6.0",
-        "behat/behat": "3.13.*",
+        "behat/mink": "^1.11.0",
+        "friends-of-behat/mink-extension": "^2.7.5",
+        "behat/mink-browserkit-driver": "^2.2.0",
+        "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+        "symfony/http-client": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+        "symfony/mime": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+        "behat/behat": "3.14.*",
         "oleg-andreyev/mink-phpwebdriver": "1.3.*",
         "filp/whoops": "^2.15"
     },
diff --git a/composer.lock b/composer.lock
index f4e0028cafe..37c00a9641c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,21 +4,21 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "ede584ad81dabcafdf0606d10a994ec6",
+    "content-hash": "93266c895599040b27580a06ca936318",
     "packages": [],
     "packages-dev": [
         {
             "name": "behat/behat",
-            "version": "v3.13.0",
+            "version": "v3.14.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Behat/Behat.git",
-                "reference": "9dd7cdb309e464ddeab095cd1a5151c2dccba4ab"
+                "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Behat/Behat/zipball/9dd7cdb309e464ddeab095cd1a5151c2dccba4ab",
-                "reference": "9dd7cdb309e464ddeab095cd1a5151c2dccba4ab",
+                "url": "https://api.github.com/repos/Behat/Behat/zipball/2a3832d9cb853a794af3a576f9e524ae460f3340",
+                "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340",
                 "shasum": ""
             },
             "require": {
@@ -27,18 +27,18 @@
                 "ext-mbstring": "*",
                 "php": "^7.2 || ^8.0",
                 "psr/container": "^1.0 || ^2.0",
-                "symfony/config": "^4.4 || ^5.0 || ^6.0",
-                "symfony/console": "^4.4 || ^5.0 || ^6.0",
-                "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0",
-                "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0",
-                "symfony/translation": "^4.4 || ^5.0 || ^6.0",
-                "symfony/yaml": "^4.4 || ^5.0 || ^6.0"
+                "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/console": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/translation": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/yaml": "^4.4 || ^5.0 || ^6.0 || ^7.0"
             },
             "require-dev": {
                 "herrera-io/box": "~1.6.1",
                 "phpspec/prophecy": "^1.15",
                 "phpunit/phpunit": "^8.5 || ^9.0",
-                "symfony/process": "^4.4 || ^5.0 || ^6.0",
+                "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0",
                 "vimeo/psalm": "^4.8"
             },
             "suggest": {
@@ -90,9 +90,9 @@
             ],
             "support": {
                 "issues": "https://github.com/Behat/Behat/issues",
-                "source": "https://github.com/Behat/Behat/tree/v3.13.0"
+                "source": "https://github.com/Behat/Behat/tree/v3.14.0"
             },
-            "time": "2023-04-18T15:40:53+00:00"
+            "time": "2023-12-09T13:55:02+00:00"
         },
         {
             "name": "behat/gherkin",
@@ -159,26 +159,28 @@
         },
         {
             "name": "behat/mink",
-            "version": "v1.10.0",
+            "version": "v1.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/minkphp/Mink.git",
-                "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5"
+                "reference": "d8527fdf8785aad38455fb426af457ab9937aece"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/minkphp/Mink/zipball/19e58905632e7cfdc5b2bafb9b950a3521af32c5",
-                "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5",
+                "url": "https://api.github.com/repos/minkphp/Mink/zipball/d8527fdf8785aad38455fb426af457ab9937aece",
+                "reference": "d8527fdf8785aad38455fb426af457ab9937aece",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.2",
-                "symfony/css-selector": "^4.4 || ^5.0 || ^6.0"
+                "symfony/css-selector": "^4.4 || ^5.0 || ^6.0 || ^7.0"
             },
             "require-dev": {
+                "phpstan/phpstan": "^1.10",
+                "phpstan/phpstan-phpunit": "^1.3",
                 "phpunit/phpunit": "^8.5.22 || ^9.5.11",
-                "symfony/error-handler": "^4.4 || ^5.0 || ^6.0",
-                "symfony/phpunit-bridge": "^5.4 || ^6.0"
+                "symfony/error-handler": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0"
             },
             "suggest": {
                 "behat/mink-browserkit-driver": "fast headless driver for any app without JS emulation",
@@ -217,37 +219,40 @@
             ],
             "support": {
                 "issues": "https://github.com/minkphp/Mink/issues",
-                "source": "https://github.com/minkphp/Mink/tree/v1.10.0"
+                "source": "https://github.com/minkphp/Mink/tree/v1.11.0"
             },
-            "time": "2022-03-28T14:22:43+00:00"
+            "time": "2023-12-09T11:23:23+00:00"
         },
         {
             "name": "behat/mink-browserkit-driver",
-            "version": "v2.1.0",
+            "version": "v2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/minkphp/MinkBrowserKitDriver.git",
-                "reference": "d2768e6c17b293d86d8fcff54cbb9e6ad938fee1"
+                "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/d2768e6c17b293d86d8fcff54cbb9e6ad938fee1",
-                "reference": "d2768e6c17b293d86d8fcff54cbb9e6ad938fee1",
+                "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/16d53476e42827ed3aafbfa4fde17a1743eafd50",
+                "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50",
                 "shasum": ""
             },
             "require": {
-                "behat/mink": "^1.9.0@dev",
+                "behat/mink": "^1.11.0@dev",
+                "ext-dom": "*",
                 "php": ">=7.2",
-                "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0",
-                "symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0"
+                "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0 || ^7.0"
             },
             "require-dev": {
                 "mink/driver-testsuite": "dev-master",
+                "phpstan/phpstan": "^1.10",
+                "phpstan/phpstan-phpunit": "^1.3",
                 "phpunit/phpunit": "^8.5 || ^9.5",
-                "symfony/error-handler": "^4.4 || ^5.0 || ^6.0",
-                "symfony/http-client": "^4.4 || ^5.0 || ^6.0",
-                "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0",
-                "symfony/mime": "^4.4 || ^5.0 || ^6.0",
+                "symfony/error-handler": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/http-client": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+                "symfony/mime": "^4.4 || ^5.0 || ^6.0 || ^7.0",
                 "yoast/phpunit-polyfills": "^1.0"
             },
             "type": "mink-driver",
@@ -282,9 +287,9 @@
             ],
             "support": {
                 "issues": "https://github.com/minkphp/MinkBrowserKitDriver/issues",
-                "source": "https://github.com/minkphp/MinkBrowserKitDriver/tree/v2.1.0"
+                "source": "https://github.com/minkphp/MinkBrowserKitDriver/tree/v2.2.0"
             },
-            "time": "2022-03-28T14:33:51+00:00"
+            "time": "2023-12-09T11:30:50+00:00"
         },
         {
             "name": "behat/transliterator",
@@ -478,23 +483,23 @@
         },
         {
             "name": "friends-of-behat/mink-extension",
-            "version": "v2.7.4",
+            "version": "v2.7.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/FriendsOfBehat/MinkExtension.git",
-                "reference": "18d5a53dff3e2c8934c53e2db8b02b7ea345fe85"
+                "reference": "854336030e11983f580f49faad1b49a1238f9846"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/18d5a53dff3e2c8934c53e2db8b02b7ea345fe85",
-                "reference": "18d5a53dff3e2c8934c53e2db8b02b7ea345fe85",
+                "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/854336030e11983f580f49faad1b49a1238f9846",
+                "reference": "854336030e11983f580f49faad1b49a1238f9846",
                 "shasum": ""
             },
             "require": {
                 "behat/behat": "^3.0.5",
                 "behat/mink": "^1.5",
                 "php": ">=7.4",
-                "symfony/config": "^4.4 || ^5.0 || ^6.0"
+                "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0"
             },
             "replace": {
                 "behat/mink-extension": "self.version"
@@ -537,9 +542,10 @@
                 "web"
             ],
             "support": {
-                "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.4"
+                "issues": "https://github.com/FriendsOfBehat/MinkExtension/issues",
+                "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.5"
             },
-            "time": "2023-10-03T13:15:12+00:00"
+            "time": "2024-01-11T09:12:02+00:00"
         },
         {
             "name": "masterminds/html5",
@@ -720,25 +726,27 @@
         },
         {
             "name": "nikic/php-parser",
-            "version": "v4.17.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
+                "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
-                "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
+                "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
                 "shasum": ""
             },
             "require": {
+                "ext-ctype": "*",
+                "ext-json": "*",
                 "ext-tokenizer": "*",
-                "php": ">=7.0"
+                "php": ">=7.4"
             },
             "require-dev": {
                 "ircmaxell/php-yacc": "^0.0.7",
-                "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
             },
             "bin": [
                 "bin/php-parse"
@@ -746,7 +754,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.9-dev"
+                    "dev-master": "5.0-dev"
                 }
             },
             "autoload": {
@@ -770,22 +778,22 @@
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
             },
-            "time": "2023-08-13T19:53:39+00:00"
+            "time": "2024-03-05T20:51:40+00:00"
         },
         {
             "name": "oleg-andreyev/mink-phpwebdriver",
-            "version": "v1.3.0",
+            "version": "v1.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/oleg-andreyev/MinkPhpWebDriver.git",
-                "reference": "d0d15960a422b6e263276281c83fc681ab75a30a"
+                "reference": "c5c2e3177515018b1f209d6b0aff1fc73d324155"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/oleg-andreyev/MinkPhpWebDriver/zipball/d0d15960a422b6e263276281c83fc681ab75a30a",
-                "reference": "d0d15960a422b6e263276281c83fc681ab75a30a",
+                "url": "https://api.github.com/repos/oleg-andreyev/MinkPhpWebDriver/zipball/c5c2e3177515018b1f209d6b0aff1fc73d324155",
+                "reference": "c5c2e3177515018b1f209d6b0aff1fc73d324155",
                 "shasum": ""
             },
             "require": {
@@ -837,24 +845,25 @@
                 "issues": "https://github.com/oleg-andreyev/MinkPhpWebDriver/issues",
                 "source": "https://github.com/oleg-andreyev/MinkPhpWebDriver/tree/v1.3.1"
             },
-            "time": "2023-07-11T22:28:02+00:00"
+            "time": "2024-01-23T18:51:27+00:00"
         },
         {
             "name": "phar-io/manifest",
-            "version": "2.0.3",
+            "version": "2.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phar-io/manifest.git",
-                "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+                "reference": "54750ef60c58e43759730615a392c31c80e23176"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
-                "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+                "reference": "54750ef60c58e43759730615a392c31c80e23176",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
+                "ext-libxml": "*",
                 "ext-phar": "*",
                 "ext-xmlwriter": "*",
                 "phar-io/version": "^3.0.1",
@@ -895,9 +904,15 @@
             "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
             "support": {
                 "issues": "https://github.com/phar-io/manifest/issues",
-                "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+                "source": "https://github.com/phar-io/manifest/tree/2.0.4"
             },
-            "time": "2021-07-20T11:28:43+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-03-03T12:33:53+00:00"
         },
         {
             "name": "phar-io/version",
@@ -1018,23 +1033,23 @@
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "9.2.29",
+            "version": "9.2.31",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76"
+                "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76",
-                "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965",
+                "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-libxml": "*",
                 "ext-xmlwriter": "*",
-                "nikic/php-parser": "^4.15",
+                "nikic/php-parser": "^4.18 || ^5.0",
                 "php": ">=7.3",
                 "phpunit/php-file-iterator": "^3.0.3",
                 "phpunit/php-text-template": "^2.0.2",
@@ -1084,7 +1099,7 @@
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
                 "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29"
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31"
             },
             "funding": [
                 {
@@ -1092,7 +1107,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-09-19T04:57:46+00:00"
+            "time": "2024-03-02T06:37:42+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -1337,16 +1352,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.5.28",
+            "version": "9.6.18",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e"
+                "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e",
-                "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04",
+                "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04",
                 "shasum": ""
             },
             "require": {
@@ -1361,7 +1376,7 @@
                 "phar-io/manifest": "^2.0.3",
                 "phar-io/version": "^3.0.2",
                 "php": ">=7.3",
-                "phpunit/php-code-coverage": "^9.2.13",
+                "phpunit/php-code-coverage": "^9.2.28",
                 "phpunit/php-file-iterator": "^3.0.5",
                 "phpunit/php-invoker": "^3.1.1",
                 "phpunit/php-text-template": "^2.0.3",
@@ -1379,8 +1394,8 @@
                 "sebastian/version": "^3.0.2"
             },
             "suggest": {
-                "ext-soap": "*",
-                "ext-xdebug": "*"
+                "ext-soap": "To be able to generate mocks based on WSDL files",
+                "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
             },
             "bin": [
                 "phpunit"
@@ -1388,7 +1403,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.5-dev"
+                    "dev-master": "9.6-dev"
                 }
             },
             "autoload": {
@@ -1419,7 +1434,8 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28"
+                "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18"
             },
             "funding": [
                 {
@@ -1435,7 +1451,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-14T12:32:24+00:00"
+            "time": "2024-03-21T12:07:32+00:00"
         },
         {
             "name": "psr/container",
@@ -1592,16 +1608,16 @@
         },
         {
             "name": "sebastian/cli-parser",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/cli-parser.git",
-                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+                "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
-                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+                "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
                 "shasum": ""
             },
             "require": {
@@ -1636,7 +1652,7 @@
             "homepage": "https://github.com/sebastianbergmann/cli-parser",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
-                "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+                "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
             },
             "funding": [
                 {
@@ -1644,7 +1660,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T06:08:49+00:00"
+            "time": "2024-03-02T06:27:43+00:00"
         },
         {
             "name": "sebastian/code-unit",
@@ -1833,20 +1849,20 @@
         },
         {
             "name": "sebastian/complexity",
-            "version": "2.0.2",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/complexity.git",
-                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+                "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
-                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
+                "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
                 "shasum": ""
             },
             "require": {
-                "nikic/php-parser": "^4.7",
+                "nikic/php-parser": "^4.18 || ^5.0",
                 "php": ">=7.3"
             },
             "require-dev": {
@@ -1878,7 +1894,7 @@
             "homepage": "https://github.com/sebastianbergmann/complexity",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/complexity/issues",
-                "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+                "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
             },
             "funding": [
                 {
@@ -1886,20 +1902,20 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T15:52:27+00:00"
+            "time": "2023-12-22T06:19:30+00:00"
         },
         {
             "name": "sebastian/diff",
-            "version": "4.0.5",
+            "version": "4.0.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
+                "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
-                "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+                "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
                 "shasum": ""
             },
             "require": {
@@ -1944,7 +1960,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/diff/issues",
-                "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
+                "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
             },
             "funding": [
                 {
@@ -1952,7 +1968,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-05-07T05:35:17+00:00"
+            "time": "2024-03-02T06:30:58+00:00"
         },
         {
             "name": "sebastian/environment",
@@ -2019,16 +2035,16 @@
         },
         {
             "name": "sebastian/exporter",
-            "version": "4.0.5",
+            "version": "4.0.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
+                "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
-                "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72",
+                "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72",
                 "shasum": ""
             },
             "require": {
@@ -2084,7 +2100,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/exporter/issues",
-                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
+                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6"
             },
             "funding": [
                 {
@@ -2092,20 +2108,20 @@
                     "type": "github"
                 }
             ],
-            "time": "2022-09-14T06:03:37+00:00"
+            "time": "2024-03-02T06:33:00+00:00"
         },
         {
             "name": "sebastian/global-state",
-            "version": "5.0.6",
+            "version": "5.0.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/global-state.git",
-                "reference": "bde739e7565280bda77be70044ac1047bc007e34"
+                "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
-                "reference": "bde739e7565280bda77be70044ac1047bc007e34",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
+                "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
                 "shasum": ""
             },
             "require": {
@@ -2148,7 +2164,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/global-state/issues",
-                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
+                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7"
             },
             "funding": [
                 {
@@ -2156,24 +2172,24 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-08-02T09:26:13+00:00"
+            "time": "2024-03-02T06:35:11+00:00"
         },
         {
             "name": "sebastian/lines-of-code",
-            "version": "1.0.3",
+            "version": "1.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/lines-of-code.git",
-                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+                "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
-                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+                "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
                 "shasum": ""
             },
             "require": {
-                "nikic/php-parser": "^4.6",
+                "nikic/php-parser": "^4.18 || ^5.0",
                 "php": ">=7.3"
             },
             "require-dev": {
@@ -2205,7 +2221,7 @@
             "homepage": "https://github.com/sebastianbergmann/lines-of-code",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
-                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
             },
             "funding": [
                 {
@@ -2213,7 +2229,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-11-28T06:42:11+00:00"
+            "time": "2023-12-22T06:20:34+00:00"
         },
         {
             "name": "sebastian/object-enumerator",
@@ -2392,16 +2408,16 @@
         },
         {
             "name": "sebastian/resource-operations",
-            "version": "3.0.3",
+            "version": "3.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/resource-operations.git",
-                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+                "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
-                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+                "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
                 "shasum": ""
             },
             "require": {
@@ -2413,7 +2429,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.0-dev"
+                    "dev-main": "3.0-dev"
                 }
             },
             "autoload": {
@@ -2434,8 +2450,7 @@
             "description": "Provides a list of PHP built-in functions that operate on resources",
             "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
-                "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+                "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
             },
             "funding": [
                 {
@@ -2443,7 +2458,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T06:45:17+00:00"
+            "time": "2024-03-14T16:00:52+00:00"
         },
         {
             "name": "sebastian/type",
@@ -2556,16 +2571,16 @@
         },
         {
             "name": "symfony/browser-kit",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/browser-kit.git",
-                "reference": "a3bb210e001580ec75e1d02b27fae3452e6bf502"
+                "reference": "495ffa2e6d17e199213f93768efa01af32bbf70e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/a3bb210e001580ec75e1d02b27fae3452e6bf502",
-                "reference": "a3bb210e001580ec75e1d02b27fae3452e6bf502",
+                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/495ffa2e6d17e199213f93768efa01af32bbf70e",
+                "reference": "495ffa2e6d17e199213f93768efa01af32bbf70e",
                 "shasum": ""
             },
             "require": {
@@ -2604,7 +2619,7 @@
             "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/browser-kit/tree/v6.4.0"
+                "source": "https://github.com/symfony/browser-kit/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2620,20 +2635,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-10-31T08:18:17+00:00"
+            "time": "2024-01-23T14:51:35+00:00"
         },
         {
             "name": "symfony/config",
-            "version": "v6.4.0",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "5d33e0fb707d603330e0edfd4691803a1253572e"
+                "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/5d33e0fb707d603330e0edfd4691803a1253572e",
-                "reference": "5d33e0fb707d603330e0edfd4691803a1253572e",
+                "url": "https://api.github.com/repos/symfony/config/zipball/6ea4affc27f2086c9d16b92ab5429ce1e3c38047",
+                "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047",
                 "shasum": ""
             },
             "require": {
@@ -2679,7 +2694,7 @@
             "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/config/tree/v6.4.0"
+                "source": "https://github.com/symfony/config/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -2695,20 +2710,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-09T08:28:32+00:00"
+            "time": "2024-02-26T07:52:26+00:00"
         },
         {
             "name": "symfony/console",
-            "version": "v6.4.1",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd"
+                "reference": "0d9e4eb5ad413075624378f474c4167ea202de78"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd",
-                "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd",
+                "url": "https://api.github.com/repos/symfony/console/zipball/0d9e4eb5ad413075624378f474c4167ea202de78",
+                "reference": "0d9e4eb5ad413075624378f474c4167ea202de78",
                 "shasum": ""
             },
             "require": {
@@ -2773,7 +2788,7 @@
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v6.4.1"
+                "source": "https://github.com/symfony/console/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -2789,20 +2804,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-30T10:54:28+00:00"
+            "time": "2024-02-22T20:27:10+00:00"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4"
+                "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/d036c6c0d0b09e24a14a35f8292146a658f986e4",
-                "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
+                "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
                 "shasum": ""
             },
             "require": {
@@ -2838,7 +2853,7 @@
             "description": "Converts CSS selectors to XPath expressions",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/css-selector/tree/v6.4.0"
+                "source": "https://github.com/symfony/css-selector/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2854,20 +2869,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-10-31T08:40:20+00:00"
+            "time": "2024-01-23T14:51:35+00:00"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v6.4.1",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "f88ff6428afbeb17cc648c8003bd608534750baf"
+                "reference": "6236e5e843cb763e9d0f74245678b994afea5363"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f88ff6428afbeb17cc648c8003bd608534750baf",
-                "reference": "f88ff6428afbeb17cc648c8003bd608534750baf",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6236e5e843cb763e9d0f74245678b994afea5363",
+                "reference": "6236e5e843cb763e9d0f74245678b994afea5363",
                 "shasum": ""
             },
             "require": {
@@ -2919,7 +2934,7 @@
             "description": "Allows you to standardize and centralize the way objects are constructed in your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/dependency-injection/tree/v6.4.1"
+                "source": "https://github.com/symfony/dependency-injection/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -2935,7 +2950,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-12-01T14:56:37+00:00"
+            "time": "2024-02-22T20:27:10+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
@@ -3006,16 +3021,16 @@
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v6.4.0",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "14ff4fd2a5c8969d6158dbe7ef5b17d6a9c6ba33"
+                "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/14ff4fd2a5c8969d6158dbe7ef5b17d6a9c6ba33",
-                "reference": "14ff4fd2a5c8969d6158dbe7ef5b17d6a9c6ba33",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
+                "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
                 "shasum": ""
             },
             "require": {
@@ -3053,7 +3068,7 @@
             "description": "Eases DOM navigation for HTML and XML documents",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/dom-crawler/tree/v6.4.0"
+                "source": "https://github.com/symfony/dom-crawler/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -3069,20 +3084,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-20T16:41:16+00:00"
+            "time": "2024-02-07T09:17:57+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6"
+                "reference": "ae9d3a6f3003a6caf56acd7466d8d52378d44fef"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d76d2632cfc2206eecb5ad2b26cd5934082941b6",
-                "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ae9d3a6f3003a6caf56acd7466d8d52378d44fef",
+                "reference": "ae9d3a6f3003a6caf56acd7466d8d52378d44fef",
                 "shasum": ""
             },
             "require": {
@@ -3133,7 +3148,7 @@
             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.0"
+                "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -3149,7 +3164,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-27T06:52:43+00:00"
+            "time": "2024-01-23T14:51:35+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
@@ -3229,16 +3244,16 @@
         },
         {
             "name": "symfony/filesystem",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59"
+                "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59",
-                "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
+                "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
                 "shasum": ""
             },
             "require": {
@@ -3272,7 +3287,7 @@
             "description": "Provides basic utilities for the filesystem",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/filesystem/tree/v6.4.0"
+                "source": "https://github.com/symfony/filesystem/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -3288,20 +3303,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-26T17:27:13+00:00"
+            "time": "2024-01-23T14:51:35+00:00"
         },
         {
             "name": "symfony/http-client",
-            "version": "v6.4.0",
+            "version": "v6.4.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-client.git",
-                "reference": "5c584530b77aa10ae216989ffc48b4bedc9c0b29"
+                "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-client/zipball/5c584530b77aa10ae216989ffc48b4bedc9c0b29",
-                "reference": "5c584530b77aa10ae216989ffc48b4bedc9c0b29",
+                "url": "https://api.github.com/repos/symfony/http-client/zipball/f3c86a60a3615f466333a11fd42010d4382a82c7",
+                "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7",
                 "shasum": ""
             },
             "require": {
@@ -3365,7 +3380,7 @@
                 "http"
             ],
             "support": {
-                "source": "https://github.com/symfony/http-client/tree/v6.4.0"
+                "source": "https://github.com/symfony/http-client/tree/v6.4.5"
             },
             "funding": [
                 {
@@ -3381,7 +3396,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-28T20:55:58+00:00"
+            "time": "2024-03-02T12:45:30+00:00"
         },
         {
             "name": "symfony/http-client-contracts",
@@ -3463,16 +3478,16 @@
         },
         {
             "name": "symfony/mime",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205"
+                "reference": "5017e0a9398c77090b7694be46f20eb796262a34"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/ca4f58b2ef4baa8f6cecbeca2573f88cd577d205",
-                "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/5017e0a9398c77090b7694be46f20eb796262a34",
+                "reference": "5017e0a9398c77090b7694be46f20eb796262a34",
                 "shasum": ""
             },
             "require": {
@@ -3527,7 +3542,7 @@
                 "mime-type"
             ],
             "support": {
-                "source": "https://github.com/symfony/mime/tree/v6.4.0"
+                "source": "https://github.com/symfony/mime/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -3543,20 +3558,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-10-17T11:49:05+00:00"
+            "time": "2024-01-30T08:32:12+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
+                "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
-                "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+                "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
                 "shasum": ""
             },
             "require": {
@@ -3570,9 +3585,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -3609,7 +3621,7 @@
                 "portable"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -3625,20 +3637,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-26T09:26:14+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-intl-grapheme",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
-                "reference": "875e90aeea2777b6f135677f618529449334a612"
+                "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
-                "reference": "875e90aeea2777b6f135677f618529449334a612",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f",
+                "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f",
                 "shasum": ""
             },
             "require": {
@@ -3649,9 +3661,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -3690,7 +3699,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -3706,20 +3715,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-26T09:26:14+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-intl-idn",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-idn.git",
-                "reference": "ecaafce9f77234a6a449d29e49267ba10499116d"
+                "reference": "a287ed7475f85bf6f61890146edbc932c0fff919"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d",
-                "reference": "ecaafce9f77234a6a449d29e49267ba10499116d",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919",
+                "reference": "a287ed7475f85bf6f61890146edbc932c0fff919",
                 "shasum": ""
             },
             "require": {
@@ -3732,9 +3741,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -3777,7 +3783,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -3793,20 +3799,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-26T09:30:37+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-intl-normalizer",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
-                "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
+                "reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
-                "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
+                "reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
                 "shasum": ""
             },
             "require": {
@@ -3817,9 +3823,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -3861,7 +3864,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -3877,20 +3880,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-26T09:26:14+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "42292d99c55abe617799667f454222c54c60e229"
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
-                "reference": "42292d99c55abe617799667f454222c54c60e229",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
                 "shasum": ""
             },
             "require": {
@@ -3904,9 +3907,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -3944,7 +3944,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -3960,20 +3960,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-28T09:04:16+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.28.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
+                "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
-                "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25",
+                "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25",
                 "shasum": ""
             },
             "require": {
@@ -3981,9 +3981,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.28-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -4020,7 +4017,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -4036,20 +4033,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-26T09:26:14+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v6.4.0",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa"
+                "reference": "710e27879e9be3395de2b98da3f52a946039f297"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/191703b1566d97a5425dc969e4350d32b8ef17aa",
-                "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa",
+                "url": "https://api.github.com/repos/symfony/process/zipball/710e27879e9be3395de2b98da3f52a946039f297",
+                "reference": "710e27879e9be3395de2b98da3f52a946039f297",
                 "shasum": ""
             },
             "require": {
@@ -4081,7 +4078,7 @@
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v6.4.0"
+                "source": "https://github.com/symfony/process/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -4097,25 +4094,25 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-17T21:06:49+00:00"
+            "time": "2024-02-20T12:31:00+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v3.4.0",
+            "version": "v3.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838"
+                "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
-                "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
+                "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
                 "shasum": ""
             },
             "require": {
                 "php": ">=8.1",
-                "psr/container": "^2.0"
+                "psr/container": "^1.1|^2.0"
             },
             "conflict": {
                 "ext-psr": "<1.1|>=2"
@@ -4163,7 +4160,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/service-contracts/tree/v3.4.0"
+                "source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
             },
             "funding": [
                 {
@@ -4179,20 +4176,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-30T20:28:31+00:00"
+            "time": "2023-12-26T14:02:43+00:00"
         },
         {
             "name": "symfony/string",
-            "version": "v6.4.0",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809"
+                "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/b45fcf399ea9c3af543a92edf7172ba21174d809",
-                "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809",
+                "url": "https://api.github.com/repos/symfony/string/zipball/4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9",
+                "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9",
                 "shasum": ""
             },
             "require": {
@@ -4249,7 +4246,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v6.4.0"
+                "source": "https://github.com/symfony/string/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -4265,20 +4262,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-28T20:41:49+00:00"
+            "time": "2024-02-01T13:16:41+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v6.4.0",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37"
+                "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37",
-                "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/bce6a5a78e94566641b2594d17e48b0da3184a8e",
+                "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e",
                 "shasum": ""
             },
             "require": {
@@ -4301,7 +4298,7 @@
                 "symfony/translation-implementation": "2.3|3.0"
             },
             "require-dev": {
-                "nikic/php-parser": "^4.13",
+                "nikic/php-parser": "^4.18|^5.0",
                 "psr/log": "^1|^2|^3",
                 "symfony/config": "^5.4|^6.0|^7.0",
                 "symfony/console": "^5.4|^6.0|^7.0",
@@ -4344,7 +4341,7 @@
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v6.4.0"
+                "source": "https://github.com/symfony/translation/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -4360,20 +4357,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-29T08:14:36+00:00"
+            "time": "2024-02-20T13:16:58+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v3.4.0",
+            "version": "v3.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5"
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5",
-                "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7",
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7",
                 "shasum": ""
             },
             "require": {
@@ -4422,7 +4419,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0"
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1"
             },
             "funding": [
                 {
@@ -4438,20 +4435,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-25T15:08:44+00:00"
+            "time": "2023-12-26T14:02:43+00:00"
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v6.4.1",
+            "version": "v6.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
-                "reference": "2d08ca6b9cc704dce525615d1e6d1788734f36d9"
+                "reference": "0bd342e24aef49fc82a21bd4eedd3e665d177e5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2d08ca6b9cc704dce525615d1e6d1788734f36d9",
-                "reference": "2d08ca6b9cc704dce525615d1e6d1788734f36d9",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0bd342e24aef49fc82a21bd4eedd3e665d177e5b",
+                "reference": "0bd342e24aef49fc82a21bd4eedd3e665d177e5b",
                 "shasum": ""
             },
             "require": {
@@ -4497,7 +4494,7 @@
                 "serialize"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-exporter/tree/v6.4.1"
+                "source": "https://github.com/symfony/var-exporter/tree/v6.4.4"
             },
             "funding": [
                 {
@@ -4513,20 +4510,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-30T10:32:10+00:00"
+            "time": "2024-02-26T08:37:45+00:00"
         },
         {
             "name": "symfony/yaml",
-            "version": "v6.4.0",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587"
+                "reference": "d75715985f0f94f978e3a8fa42533e10db921b90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/4f9237a1bb42455d609e6687d2613dde5b41a587",
-                "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/d75715985f0f94f978e3a8fa42533e10db921b90",
+                "reference": "d75715985f0f94f978e3a8fa42533e10db921b90",
                 "shasum": ""
             },
             "require": {
@@ -4569,7 +4566,7 @@
             "description": "Loads and dumps YAML files",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/yaml/tree/v6.4.0"
+                "source": "https://github.com/symfony/yaml/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -4585,20 +4582,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-11-06T11:00:25+00:00"
+            "time": "2024-01-23T14:51:35+00:00"
         },
         {
             "name": "theseer/tokenizer",
-            "version": "1.2.2",
+            "version": "1.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
+                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
-                "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
                 "shasum": ""
             },
             "require": {
@@ -4627,7 +4624,7 @@
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
             "support": {
                 "issues": "https://github.com/theseer/tokenizer/issues",
-                "source": "https://github.com/theseer/tokenizer/tree/1.2.2"
+                "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
             },
             "funding": [
                 {
@@ -4635,7 +4632,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-11-20T00:12:19+00:00"
+            "time": "2024-03-03T12:36:25+00:00"
         }
     ],
     "aliases": [],
diff --git a/course/format/tests/external/update_course_test.php b/course/format/tests/external/update_course_test.php
index 56d64f889bd..754b355cafb 100644
--- a/course/format/tests/external/update_course_test.php
+++ b/course/format/tests/external/update_course_test.php
@@ -93,7 +93,7 @@ class update_course_test extends \externallib_advanced_testcase {
         $update = $this->find_update($results, $expected['action'], 'cm', $activity->cmid);
         $this->assertNotEmpty($update);
         if ($expected['visible'] === null) {
-            $this->assertObjectNotHasAttribute('visible', $update->fields);
+            $this->assertObjectNotHasProperty('visible', $update->fields);
         } else {
             $this->assertEquals($expected['visible'], $update->fields->visible);
         }
diff --git a/course/format/tests/output/activitybadge_test.php b/course/format/tests/output/activitybadge_test.php
index 98f2dfe3fdd..b20e955b24b 100644
--- a/course/format/tests/output/activitybadge_test.php
+++ b/course/format/tests/output/activitybadge_test.php
@@ -176,31 +176,31 @@ class activitybadge_test extends \advanced_testcase {
         ?array $extra = null
     ): void {
         if (is_null($content)) {
-            $this->assertObjectNotHasAttribute('badgecontent', $result);
+            $this->assertObjectNotHasProperty('badgecontent', $result);
         } else {
             $this->assertEquals($content, $result->badgecontent);
         }
 
         if (is_null($style)) {
-            $this->assertObjectNotHasAttribute('badgestyle', $result);
+            $this->assertObjectNotHasProperty('badgestyle', $result);
         } else {
             $this->assertEquals($style, $result->badgestyle);
         }
 
         if (is_null($url)) {
-            $this->assertObjectNotHasAttribute('badgeurl', $result);
+            $this->assertObjectNotHasProperty('badgeurl', $result);
         } else {
             $this->assertEquals($url, $result->badgeurl);
         }
 
         if (is_null($elementid)) {
-            $this->assertObjectNotHasAttribute('badgeelementid', $result);
+            $this->assertObjectNotHasProperty('badgeelementid', $result);
         } else {
             $this->assertEquals($elementid, $result->badgeelementid);
         }
 
         if (is_null($extra)) {
-            $this->assertObjectNotHasAttribute('badgeextraattributes', $result);
+            $this->assertObjectNotHasProperty('badgeextraattributes', $result);
         } else {
             $this->assertEquals($extra, $result->badgeextraattributes);
         }
diff --git a/course/tests/exporters_content_item_test.php b/course/tests/exporters_content_item_test.php
index 63b10a33f0e..307a2e7ec36 100644
--- a/course/tests/exporters_content_item_test.php
+++ b/course/tests/exporters_content_item_test.php
@@ -54,23 +54,23 @@ class exporters_content_item_test extends \advanced_testcase {
         $renderer = $PAGE->get_renderer('core');
         $exporteditem = $ciexporter->export($renderer);
 
-        $this->assertObjectHasAttribute('id', $exporteditem);
+        $this->assertObjectHasProperty('id', $exporteditem);
         $this->assertEquals($exporteditem->id, $contentitem->get_id());
-        $this->assertObjectHasAttribute('name', $exporteditem);
+        $this->assertObjectHasProperty('name', $exporteditem);
         $this->assertEquals($exporteditem->name, $contentitem->get_name());
-        $this->assertObjectHasAttribute('title', $exporteditem);
+        $this->assertObjectHasProperty('title', $exporteditem);
         $this->assertEquals($exporteditem->title, $contentitem->get_title()->get_value());
-        $this->assertObjectHasAttribute('link', $exporteditem);
+        $this->assertObjectHasProperty('link', $exporteditem);
         $this->assertEquals($exporteditem->link, $contentitem->get_link()->out(false));
-        $this->assertObjectHasAttribute('icon', $exporteditem);
+        $this->assertObjectHasProperty('icon', $exporteditem);
         $this->assertEquals($exporteditem->icon, $contentitem->get_icon());
-        $this->assertObjectHasAttribute('help', $exporteditem);
+        $this->assertObjectHasProperty('help', $exporteditem);
         $this->assertEquals($exporteditem->help, format_text($contentitem->get_help(), FORMAT_MARKDOWN));
-        $this->assertObjectHasAttribute('archetype', $exporteditem);
+        $this->assertObjectHasProperty('archetype', $exporteditem);
         $this->assertEquals($exporteditem->archetype, $contentitem->get_archetype());
-        $this->assertObjectHasAttribute('componentname', $exporteditem);
+        $this->assertObjectHasProperty('componentname', $exporteditem);
         $this->assertEquals($exporteditem->componentname, $contentitem->get_component_name());
-        $this->assertObjectHasAttribute('legacyitem', $exporteditem);
+        $this->assertObjectHasProperty('legacyitem', $exporteditem);
         $this->assertFalse($exporteditem->legacyitem);
         $this->assertEquals($exporteditem->purpose, $contentitem->get_purpose());
         $this->assertEquals($exporteditem->branded, $contentitem->is_branded());
@@ -102,24 +102,24 @@ class exporters_content_item_test extends \advanced_testcase {
         $renderer = $PAGE->get_renderer('core');
         $exporteditem = $ciexporter->export($renderer);
 
-        $this->assertObjectHasAttribute('id', $exporteditem);
+        $this->assertObjectHasProperty('id', $exporteditem);
         $this->assertEquals($exporteditem->id, $contentitem->get_id());
-        $this->assertObjectHasAttribute('name', $exporteditem);
+        $this->assertObjectHasProperty('name', $exporteditem);
         $this->assertEquals($exporteditem->name, $contentitem->get_name());
-        $this->assertObjectHasAttribute('title', $exporteditem);
+        $this->assertObjectHasProperty('title', $exporteditem);
         $this->assertEquals($exporteditem->title, $contentitem->get_title()->get_value());
-        $this->assertObjectHasAttribute('link', $exporteditem);
+        $this->assertObjectHasProperty('link', $exporteditem);
         $this->assertEquals($exporteditem->link, $contentitem->get_link()->out(false));
-        $this->assertObjectHasAttribute('icon', $exporteditem);
+        $this->assertObjectHasProperty('icon', $exporteditem);
         $this->assertEquals($exporteditem->icon, $contentitem->get_icon());
-        $this->assertObjectHasAttribute('help', $exporteditem);
+        $this->assertObjectHasProperty('help', $exporteditem);
         $this->assertEquals($exporteditem->help, format_text($contentitem->get_help(), FORMAT_MARKDOWN));
-        $this->assertObjectHasAttribute('archetype', $exporteditem);
+        $this->assertObjectHasProperty('archetype', $exporteditem);
         $this->assertEquals($exporteditem->archetype, $contentitem->get_archetype());
-        $this->assertObjectHasAttribute('componentname', $exporteditem);
+        $this->assertObjectHasProperty('componentname', $exporteditem);
         $this->assertEquals($exporteditem->componentname, $contentitem->get_component_name());
         // Most important, is this a legacy item?
-        $this->assertObjectHasAttribute('legacyitem', $exporteditem);
+        $this->assertObjectHasProperty('legacyitem', $exporteditem);
         $this->assertTrue($exporteditem->legacyitem);
     }
 }
diff --git a/course/tests/exporters_content_items_test.php b/course/tests/exporters_content_items_test.php
index 9e4b6db53be..9563a7e587f 100644
--- a/course/tests/exporters_content_items_test.php
+++ b/course/tests/exporters_content_items_test.php
@@ -53,16 +53,16 @@ class exporters_content_items_test extends \advanced_testcase {
         $renderer = $PAGE->get_renderer('core');
         $exportedcontentitems = $ciexporter->export($renderer);
 
-        $this->assertObjectHasAttribute('content_items', $exportedcontentitems);
+        $this->assertObjectHasProperty('content_items', $exportedcontentitems);
         foreach ($exportedcontentitems->content_items as $key => $dto) {
-            $this->assertObjectHasAttribute('id', $dto);
-            $this->assertObjectHasAttribute('name', $dto);
-            $this->assertObjectHasAttribute('title', $dto);
-            $this->assertObjectHasAttribute('link', $dto);
-            $this->assertObjectHasAttribute('icon', $dto);
-            $this->assertObjectHasAttribute('help', $dto);
-            $this->assertObjectHasAttribute('archetype', $dto);
-            $this->assertObjectHasAttribute('componentname', $dto);
+            $this->assertObjectHasProperty('id', $dto);
+            $this->assertObjectHasProperty('name', $dto);
+            $this->assertObjectHasProperty('title', $dto);
+            $this->assertObjectHasProperty('link', $dto);
+            $this->assertObjectHasProperty('icon', $dto);
+            $this->assertObjectHasProperty('help', $dto);
+            $this->assertObjectHasProperty('archetype', $dto);
+            $this->assertObjectHasProperty('componentname', $dto);
         }
     }
 }
diff --git a/course/tests/privacy/provider_test.php b/course/tests/privacy/provider_test.php
index 872bd12d986..81fd442c946 100644
--- a/course/tests/privacy/provider_test.php
+++ b/course/tests/privacy/provider_test.php
@@ -193,10 +193,10 @@ class provider_test extends \core_privacy\tests\provider_testcase {
         $writer = \core_privacy\local\request\writer::with_context($context1);
         $this->assertTrue($writer->has_any_data());
         $writerdata = $writer->get_data();
-        $this->assertObjectHasAttribute('fullname', $writerdata);
-        $this->assertObjectHasAttribute('shortname', $writerdata);
-        $this->assertObjectHasAttribute('idnumber', $writerdata);
-        $this->assertObjectHasAttribute('summary', $writerdata);
+        $this->assertObjectHasProperty('fullname', $writerdata);
+        $this->assertObjectHasProperty('shortname', $writerdata);
+        $this->assertObjectHasProperty('idnumber', $writerdata);
+        $this->assertObjectHasProperty('summary', $writerdata);
     }
 
     /**
@@ -226,10 +226,10 @@ class provider_test extends \core_privacy\tests\provider_testcase {
         $writer = \core_privacy\local\request\writer::with_context($context1);
         $this->assertTrue($writer->has_any_data());
         $writerdata = $writer->get_data();
-        $this->assertObjectHasAttribute('fullname', $writerdata);
-        $this->assertObjectHasAttribute('shortname', $writerdata);
-        $this->assertObjectHasAttribute('idnumber', $writerdata);
-        $this->assertObjectHasAttribute('summary', $writerdata);
+        $this->assertObjectHasProperty('fullname', $writerdata);
+        $this->assertObjectHasProperty('shortname', $writerdata);
+        $this->assertObjectHasProperty('idnumber', $writerdata);
+        $this->assertObjectHasProperty('summary', $writerdata);
     }
 
     /**
diff --git a/course/tests/services_content_item_service_test.php b/course/tests/services_content_item_service_test.php
index e438a8dcaa9..acdb662c592 100644
--- a/course/tests/services_content_item_service_test.php
+++ b/course/tests/services_content_item_service_test.php
@@ -51,14 +51,14 @@ class services_content_item_service_test extends \advanced_testcase {
         $contentitems = $cis->get_content_items_for_user_in_course($user, $course);
 
         foreach ($contentitems as $key => $contentitem) {
-            $this->assertObjectHasAttribute('id', $contentitem);
-            $this->assertObjectHasAttribute('name', $contentitem);
-            $this->assertObjectHasAttribute('title', $contentitem);
-            $this->assertObjectHasAttribute('link', $contentitem);
-            $this->assertObjectHasAttribute('icon', $contentitem);
-            $this->assertObjectHasAttribute('help', $contentitem);
-            $this->assertObjectHasAttribute('archetype', $contentitem);
-            $this->assertObjectHasAttribute('componentname', $contentitem);
+            $this->assertObjectHasProperty('id', $contentitem);
+            $this->assertObjectHasProperty('name', $contentitem);
+            $this->assertObjectHasProperty('title', $contentitem);
+            $this->assertObjectHasProperty('link', $contentitem);
+            $this->assertObjectHasProperty('icon', $contentitem);
+            $this->assertObjectHasProperty('help', $contentitem);
+            $this->assertObjectHasProperty('archetype', $contentitem);
+            $this->assertObjectHasProperty('componentname', $contentitem);
         }
     }
 
diff --git a/course/tests/task/content_notification_task_test.php b/course/tests/task/content_notification_task_test.php
index 9c88025fd11..f5ca17005bf 100644
--- a/course/tests/task/content_notification_task_test.php
+++ b/course/tests/task/content_notification_task_test.php
@@ -120,8 +120,8 @@ class content_notification_task_test extends \advanced_testcase {
 
             $messagecustomdata = json_decode($message->customdata);
             $this->assertEquals($course->id, $messagecustomdata->courseid);
-            $this->assertObjectHasAttribute('notificationiconurl', $messagecustomdata);
-            $this->assertObjectHasAttribute('notificationpictureurl', $messagecustomdata);
+            $this->assertObjectHasProperty('notificationiconurl', $messagecustomdata);
+            $this->assertObjectHasProperty('notificationpictureurl', $messagecustomdata);
         }
 
         // Now, set the course to not visible.
diff --git a/enrol/lti/tests/local/ltiadvantage/service/tool_launch_service_test.php b/enrol/lti/tests/local/ltiadvantage/service/tool_launch_service_test.php
index 6558e44ec43..c2239b20c01 100644
--- a/enrol/lti/tests/local/ltiadvantage/service/tool_launch_service_test.php
+++ b/enrol/lti/tests/local/ltiadvantage/service/tool_launch_service_test.php
@@ -514,7 +514,7 @@ class tool_launch_service_test extends \lti_advantage_testcase {
 
         // Instructors aren't subject to forceembed.
         $launchservice->user_launches_tool($instructoruser, $mockinstructorlaunch);
-        $this->assertObjectNotHasAttribute('forcepagelayout', $SESSION);
+        $this->assertObjectNotHasProperty('forcepagelayout', $SESSION);
 
         // Learners are.
         $launchservice->user_launches_tool($learneruser, $mocklearnerlaunch);
diff --git a/enrol/tests/course_enrolment_manager_test.php b/enrol/tests/course_enrolment_manager_test.php
index 01fb9a6b983..70c4bf36c7b 100644
--- a/enrol/tests/course_enrolment_manager_test.php
+++ b/enrol/tests/course_enrolment_manager_test.php
@@ -296,7 +296,7 @@ class course_enrolment_manager_test extends \advanced_testcase {
         $this->assertEquals('Smart suit', $user->imagealt);
 
         // But not some random other field like city.
-        $this->assertObjectNotHasAttribute('city', $user);
+        $this->assertObjectNotHasProperty('city', $user);
     }
 
     /**
@@ -330,7 +330,7 @@ class course_enrolment_manager_test extends \advanced_testcase {
         $this->assertEquals('Smart suit', $user->imagealt);
 
         // But not some random other field like city.
-        $this->assertObjectNotHasAttribute('city', $user);
+        $this->assertObjectNotHasProperty('city', $user);
     }
 
     /**
@@ -367,7 +367,7 @@ class course_enrolment_manager_test extends \advanced_testcase {
         $this->assertEquals('Smart suit', $user->imagealt);
 
         // But not some random other field like city.
-        $this->assertObjectNotHasAttribute('city', $user);
+        $this->assertObjectNotHasProperty('city', $user);
     }
 
     /**
diff --git a/enrol/tests/enrollib_test.php b/enrol/tests/enrollib_test.php
index f394ddca05e..add5167a509 100644
--- a/enrol/tests/enrollib_test.php
+++ b/enrol/tests/enrollib_test.php
@@ -952,26 +952,26 @@ class enrollib_test extends advanced_testcase {
         $this->assertEquals([$course1->id, $course2->id, $course3->id], array_keys($courses));
 
         // Check fields parameter still works. Fields default (certain base fields).
-        $this->assertObjectHasAttribute('id', $courses[$course3->id]);
-        $this->assertObjectHasAttribute('shortname', $courses[$course3->id]);
-        $this->assertObjectNotHasAttribute('summary', $courses[$course3->id]);
+        $this->assertObjectHasProperty('id', $courses[$course3->id]);
+        $this->assertObjectHasProperty('shortname', $courses[$course3->id]);
+        $this->assertObjectNotHasProperty('summary', $courses[$course3->id]);
 
         // Specified fields (one, string).
         $courses = enrol_get_my_courses('summary', 'id', 0, [], true);
-        $this->assertObjectHasAttribute('id', $courses[$course3->id]);
-        $this->assertObjectHasAttribute('shortname', $courses[$course3->id]);
-        $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
-        $this->assertObjectNotHasAttribute('summaryformat', $courses[$course3->id]);
+        $this->assertObjectHasProperty('id', $courses[$course3->id]);
+        $this->assertObjectHasProperty('shortname', $courses[$course3->id]);
+        $this->assertObjectHasProperty('summary', $courses[$course3->id]);
+        $this->assertObjectNotHasProperty('summaryformat', $courses[$course3->id]);
 
         // Specified fields (two, string).
         $courses = enrol_get_my_courses('summary, summaryformat', 'id', 0, [], true);
-        $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
-        $this->assertObjectHasAttribute('summaryformat', $courses[$course3->id]);
+        $this->assertObjectHasProperty('summary', $courses[$course3->id]);
+        $this->assertObjectHasProperty('summaryformat', $courses[$course3->id]);
 
         // Specified fields (two, array).
         $courses = enrol_get_my_courses(['summary', 'summaryformat'], 'id', 0, [], true);
-        $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
-        $this->assertObjectHasAttribute('summaryformat', $courses[$course3->id]);
+        $this->assertObjectHasProperty('summary', $courses[$course3->id]);
+        $this->assertObjectHasProperty('summaryformat', $courses[$course3->id]);
 
         // By default, courses are ordered by sortorder - which by default is most recent first.
         $courses = enrol_get_my_courses(null, null, 0, [], true);
diff --git a/favourites/tests/repository_test.php b/favourites/tests/repository_test.php
index c83efcbcc29..d949822eadf 100644
--- a/favourites/tests/repository_test.php
+++ b/favourites/tests/repository_test.php
@@ -67,13 +67,13 @@ class repository_test extends \advanced_testcase {
 
         // Verify we get the record back.
         $this->assertInstanceOf(favourite::class, $favourite);
-        $this->assertObjectHasAttribute('id', $favourite);
+        $this->assertObjectHasProperty('id', $favourite);
         $this->assertEquals('core_course', $favourite->component);
         $this->assertEquals('course', $favourite->itemtype);
 
         // Verify the returned object has additional properties, created as part of the add.
-        $this->assertObjectHasAttribute('ordering', $favourite);
-        $this->assertObjectHasAttribute('timecreated', $favourite);
+        $this->assertObjectHasProperty('ordering', $favourite);
+        $this->assertObjectHasProperty('timecreated', $favourite);
         $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
 
         // Try to save the same record again and confirm the store throws an exception.
@@ -137,8 +137,8 @@ class repository_test extends \advanced_testcase {
             $this->assertEquals('course', $favourite->itemtype);
 
             // Verify the returned object has additional properties, created as part of the add.
-            $this->assertObjectHasAttribute('ordering', $favourite);
-            $this->assertObjectHasAttribute('timecreated', $favourite);
+            $this->assertObjectHasProperty('ordering', $favourite);
+            $this->assertObjectHasProperty('timecreated', $favourite);
             $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
         }
 
@@ -167,7 +167,7 @@ class repository_test extends \advanced_testcase {
         // Now, from the repo, get the single favourite we just created, by id.
         $userfavourite = $favouritesrepo->find($favourite->id);
         $this->assertInstanceOf(favourite::class, $userfavourite);
-        $this->assertObjectHasAttribute('timecreated', $userfavourite);
+        $this->assertObjectHasProperty('timecreated', $userfavourite);
 
         // Try to get a favourite we know doesn't exist.
         // We expect an exception in this case.
@@ -209,8 +209,8 @@ class repository_test extends \advanced_testcase {
         $this->assertCount(4, $favourites);
         foreach ($favourites as $fav) {
             $this->assertInstanceOf(favourite::class, $fav);
-            $this->assertObjectHasAttribute('id', $fav);
-            $this->assertObjectHasAttribute('timecreated', $fav);
+            $this->assertObjectHasProperty('id', $fav);
+            $this->assertObjectHasProperty('timecreated', $fav);
         }
     }
 
diff --git a/favourites/tests/user_favourite_service_test.php b/favourites/tests/user_favourite_service_test.php
index 580ba3b8910..3743a6583c9 100644
--- a/favourites/tests/user_favourite_service_test.php
+++ b/favourites/tests/user_favourite_service_test.php
@@ -200,7 +200,7 @@ class user_favourite_service_test extends \advanced_testcase {
 
         // Favourite a course.
         $favourite1 = $user1service->create_favourite('core_course', 'course', $course1context->instanceid, $course1context);
-        $this->assertObjectHasAttribute('id', $favourite1);
+        $this->assertObjectHasProperty('id', $favourite1);
 
         // Try to favourite the same course again.
         $this->expectException('moodle_exception');
diff --git a/lib/form/tests/filetypes_util_test.php b/lib/form/tests/filetypes_util_test.php
index 5bf2d73a45d..f1702741d37 100644
--- a/lib/form/tests/filetypes_util_test.php
+++ b/lib/form/tests/filetypes_util_test.php
@@ -281,8 +281,8 @@ class filetypes_util_test extends advanced_testcase {
         $data = $util->data_for_browser();
         $this->assertContainsOnly('object', $data);
         foreach ($data as $group) {
-            $this->assertObjectHasAttribute('key', $group);
-            $this->assertObjectHasAttribute('types', $group);
+            $this->assertObjectHasProperty('key', $group);
+            $this->assertObjectHasProperty('types', $group);
             if ($group->key !== '') {
                 $this->assertTrue($group->selectable);
             }
diff --git a/lib/phpunit/tests/advanced_test.php b/lib/phpunit/tests/advanced_test.php
index 6aa4ba18ccb..530fdd04af9 100644
--- a/lib/phpunit/tests/advanced_test.php
+++ b/lib/phpunit/tests/advanced_test.php
@@ -120,7 +120,7 @@ class advanced_test extends \advanced_testcase {
         // Ensure session is reset after setUser, as it may contain extra info.
         $SESSION->sometestvalue = true;
         $this->setUser($user);
-        $this->assertObjectNotHasAttribute('sometestvalue', $SESSION);
+        $this->assertObjectNotHasProperty('sometestvalue', $SESSION);
     }
 
     public function test_set_admin_user() {
@@ -369,7 +369,15 @@ class advanced_test extends \advanced_testcase {
         ]);
 
         if ($phpwarn) {
-            $this->expectWarning();
+            // Let's convert the warnings into an assert-able exception.
+            set_error_handler(
+                static function ($errno, $errstr) {
+                    restore_error_handler();
+                    throw new \Exception($errstr, $errno);
+                },
+                E_WARNING // Or any other specific E_ that we want to assert.
+            );
+            $this->expectException(\Exception::class);
         }
         $this->assertEventContextNotUsed($event);
     }
diff --git a/lib/tests/accesslib_test.php b/lib/tests/accesslib_test.php
index 8b72994b1cc..cb38c113e70 100644
--- a/lib/tests/accesslib_test.php
+++ b/lib/tests/accesslib_test.php
@@ -1685,23 +1685,23 @@ class accesslib_test extends advanced_testcase {
         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
         $this->assertCount(2, $users);
         $this->assertArrayHasKey($user1->id, $users);
-        $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
-        $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
+        $this->assertObjectHasProperty('lastname', $users[$user1->id]);
+        $this->assertObjectHasProperty('firstname', $users[$user1->id]);
         $this->assertArrayHasKey($user3->id, $users);
-        $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
-        $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
+        $this->assertObjectHasProperty('lastname', $users[$user3->id]);
+        $this->assertObjectHasProperty('firstname', $users[$user3->id]);
 
         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
         $this->assertCount(2, $users);
         $this->assertArrayHasKey($user1->id, $users);
-        $this->assertObjectHasAttribute('id_alias', $users[$user1->id]);
-        $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
-        $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
+        $this->assertObjectHasProperty('id_alias', $users[$user1->id]);
+        $this->assertObjectHasProperty('lastname', $users[$user1->id]);
+        $this->assertObjectHasProperty('firstname', $users[$user1->id]);
         $this->assertArrayHasKey($user3->id, $users);
-        $this->assertObjectHasAttribute('id_alias', $users[$user3->id]);
-        $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
-        $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
+        $this->assertObjectHasProperty('id_alias', $users[$user3->id]);
+        $this->assertObjectHasProperty('lastname', $users[$user3->id]);
+        $this->assertObjectHasProperty('firstname', $users[$user3->id]);
 
         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
         $this->assertCount(1, $users);
diff --git a/lib/tests/cron_test.php b/lib/tests/cron_test.php
index b8760a3f4ed..cc2673da217 100644
--- a/lib/tests/cron_test.php
+++ b/lib/tests/cron_test.php
@@ -76,7 +76,7 @@ class cron_test extends \advanced_testcase {
         $this->assertSame($user1->id, $USER->id);
         $this->assertSame($PAGE->context, \context_course::instance($SITE->id));
         $this->assertNotSame($adminsession, $SESSION);
-        $this->assertObjectNotHasAttribute('test1', $SESSION);
+        $this->assertObjectNotHasProperty('test1', $SESSION);
         $this->assertEmpty((array)$SESSION);
         $usersession1 = $SESSION;
         $SESSION->test2 = true;
diff --git a/lib/tests/datalib_test.php b/lib/tests/datalib_test.php
index 134176da19c..29e04102ad2 100644
--- a/lib/tests/datalib_test.php
+++ b/lib/tests/datalib_test.php
@@ -418,7 +418,7 @@ class datalib_test extends \advanced_testcase {
         $this->assertSame('folder', $cm->modname);
         $this->assertSame($folder1a->id, $cm->instance);
         $this->assertSame($folder1a->course, $cm->course);
-        $this->assertObjectNotHasAttribute('sectionnum', $cm);
+        $this->assertObjectNotHasProperty('sectionnum', $cm);
 
         $this->assertEquals($cm, get_coursemodule_from_id('', $folder1a->cmid));
         $this->assertEquals($cm, get_coursemodule_from_id('folder', $folder1a->cmid, $course1->id));
@@ -483,7 +483,7 @@ class datalib_test extends \advanced_testcase {
         $this->assertSame('folder', $cm->modname);
         $this->assertSame($folder1a->id, $cm->instance);
         $this->assertSame($folder1a->course, $cm->course);
-        $this->assertObjectNotHasAttribute('sectionnum', $cm);
+        $this->assertObjectNotHasProperty('sectionnum', $cm);
 
         $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, $course1->id));
         $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, 0));
@@ -553,17 +553,17 @@ class datalib_test extends \advanced_testcase {
         $this->assertSame('folder', $cm->modname);
         $this->assertSame($folder1a->id, $cm->instance);
         $this->assertSame($folder1a->course, $cm->course);
-        $this->assertObjectNotHasAttribute('sectionnum', $cm);
-        $this->assertObjectNotHasAttribute('revision', $cm);
-        $this->assertObjectNotHasAttribute('display', $cm);
+        $this->assertObjectNotHasProperty('sectionnum', $cm);
+        $this->assertObjectNotHasProperty('revision', $cm);
+        $this->assertObjectNotHasProperty('display', $cm);
 
         $cm = $modules[$folder1b->cmid];
         $this->assertSame('folder', $cm->modname);
         $this->assertSame($folder1b->id, $cm->instance);
         $this->assertSame($folder1b->course, $cm->course);
-        $this->assertObjectNotHasAttribute('sectionnum', $cm);
-        $this->assertObjectNotHasAttribute('revision', $cm);
-        $this->assertObjectNotHasAttribute('display', $cm);
+        $this->assertObjectNotHasProperty('sectionnum', $cm);
+        $this->assertObjectNotHasProperty('revision', $cm);
+        $this->assertObjectNotHasProperty('display', $cm);
 
         $modules = get_coursemodules_in_course('folder', $course1->id, 'revision, display');
         $this->assertCount(2, $modules);
@@ -572,9 +572,9 @@ class datalib_test extends \advanced_testcase {
         $this->assertSame('folder', $cm->modname);
         $this->assertSame($folder1a->id, $cm->instance);
         $this->assertSame($folder1a->course, $cm->course);
-        $this->assertObjectNotHasAttribute('sectionnum', $cm);
-        $this->assertObjectHasAttribute('revision', $cm);
-        $this->assertObjectHasAttribute('display', $cm);
+        $this->assertObjectNotHasProperty('sectionnum', $cm);
+        $this->assertObjectHasProperty('revision', $cm);
+        $this->assertObjectHasProperty('display', $cm);
 
         $modules = get_coursemodules_in_course('label', $course1->id);
         $this->assertCount(0, $modules);
@@ -842,11 +842,11 @@ class datalib_test extends \advanced_testcase {
         $this->assertEquals('user_a@example.com', $results[$userids[0]]->email);
         $this->assertEquals(1, $results[$userids[0]]->confirmed);
         $this->assertEquals('a_first', $results[$userids[0]]->firstname);
-        $this->assertObjectHasAttribute('firstnamephonetic', $results[$userids[0]]);
+        $this->assertObjectHasProperty('firstnamephonetic', $results[$userids[0]]);
 
         // Should not have the custom field or department because no context specified.
-        $this->assertObjectNotHasAttribute('department', $results[$userids[0]]);
-        $this->assertObjectNotHasAttribute('profile_field_specialid', $results[$userids[0]]);
+        $this->assertObjectNotHasProperty('department', $results[$userids[0]]);
+        $this->assertObjectNotHasProperty('profile_field_specialid', $results[$userids[0]]);
 
         // Check sorting.
         $results = get_users_listing('username', 'DESC');
@@ -867,8 +867,8 @@ class datalib_test extends \advanced_testcase {
         // specify a context AND have permissions.
         $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null,
                 \context_system::instance());
-        $this->assertObjectNotHasAttribute('department', $results[$userids[0]]);
-        $this->assertObjectNotHasAttribute('profile_field_specialid', $results[$userids[0]]);
+        $this->assertObjectNotHasProperty('department', $results[$userids[0]]);
+        $this->assertObjectNotHasProperty('profile_field_specialid', $results[$userids[0]]);
         $this->setAdminUser();
         $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null,
                 \context_system::instance());
diff --git a/lib/tests/filetypes_test.php b/lib/tests/filetypes_test.php
index 478de231f16..7f3bf48ec2c 100644
--- a/lib/tests/filetypes_test.php
+++ b/lib/tests/filetypes_test.php
@@ -214,36 +214,36 @@ class filetypes_test extends \advanced_testcase {
         $this->resetAfterTest();
 
         // The custom filetypes setting is empty to start with.
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
 
         // Add a custom filetype, then delete it.
         core_filetypes::add_type('frog', 'application/x-frog', 'document');
-        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectHasProperty('customfiletypes', $CFG);
         core_filetypes::delete_type('frog');
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
 
         // Change a standard filetype, then change it back.
         core_filetypes::update_type('asm', 'asm', 'text/plain', 'document');
-        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectHasProperty('customfiletypes', $CFG);
         core_filetypes::update_type('asm', 'asm', 'text/plain', 'sourcecode');
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
 
         // Delete a standard filetype, then add it back (the same).
         core_filetypes::delete_type('asm');
-        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectHasProperty('customfiletypes', $CFG);
         core_filetypes::add_type('asm', 'text/plain', 'sourcecode');
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
 
         // Revert a changed type.
         core_filetypes::update_type('asm', 'asm', 'text/plain', 'document');
-        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectHasProperty('customfiletypes', $CFG);
         core_filetypes::revert_type_to_default('asm');
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
 
         // Revert a deleted type.
         core_filetypes::delete_type('asm');
-        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectHasProperty('customfiletypes', $CFG);
         core_filetypes::revert_type_to_default('asm');
-        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+        $this->assertObjectNotHasProperty('customfiletypes', $CFG);
     }
 }
diff --git a/lib/tests/grouplib_test.php b/lib/tests/grouplib_test.php
index ea02fe88a85..378d9494469 100644
--- a/lib/tests/grouplib_test.php
+++ b/lib/tests/grouplib_test.php
@@ -656,9 +656,9 @@ class grouplib_test extends \advanced_testcase {
         // Get the data.
         $data = groups_get_course_data($course->id);
         $this->assertInstanceOf('stdClass', $data);
-        $this->assertObjectHasAttribute('groups', $data);
-        $this->assertObjectHasAttribute('groupings', $data);
-        $this->assertObjectHasAttribute('mappings', $data);
+        $this->assertObjectHasProperty('groups', $data);
+        $this->assertObjectHasProperty('groupings', $data);
+        $this->assertObjectHasProperty('mappings', $data);
 
         // Test we have the expected items returns.
         $this->assertCount(4, $data->groups);
diff --git a/lib/tests/lock/timing_wrapper_lock_factory_test.php b/lib/tests/lock/timing_wrapper_lock_factory_test.php
index a05815d4cd7..f1918bff302 100644
--- a/lib/tests/lock/timing_wrapper_lock_factory_test.php
+++ b/lib/tests/lock/timing_wrapper_lock_factory_test.php
@@ -50,7 +50,7 @@ class timing_wrapper_lock_factory_test extends \advanced_testcase {
         $duration = microtime(true) - $before;
 
         // Confirm that perf info is now logged and appears plausible.
-        $this->assertObjectHasAttribute('locks', $PERF);
+        $this->assertObjectHasProperty('locks', $PERF);
         $this->assertEquals('phpunit', $PERF->locks[0]->type);
         $this->assertEquals('frog', $PERF->locks[0]->resource);
         $this->assertTrue($PERF->locks[0]->wait <= $duration);
diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php
index 1b277bb9e82..a04c9e1faa8 100644
--- a/lib/tests/moodlelib_test.php
+++ b/lib/tests/moodlelib_test.php
@@ -2145,11 +2145,7 @@ EOF;
     public function test_get_string_limitation() {
         // This is one of the limitations to the lang_string class. It can't be
         // used as a key.
-        if (PHP_VERSION_ID >= 80000) {
-            $this->expectException(\TypeError::class);
-        } else {
-            $this->expectWarning();
-        }
+        $this->expectException(\TypeError::class);
         $array = array(get_string('yes', null, null, true) => 'yes');
     }
 
@@ -3159,8 +3155,8 @@ EOF;
         $this->assertTimeCurrent($USER->currentlogin);
         $this->assertSame(sesskey(), $USER->sesskey);
         $this->assertTimeCurrent($USER->preference['_lastloaded']);
-        $this->assertObjectNotHasAttribute('password', $USER);
-        $this->assertObjectNotHasAttribute('description', $USER);
+        $this->assertObjectNotHasProperty('password', $USER);
+        $this->assertObjectNotHasProperty('description', $USER);
     }
 
     /**
diff --git a/lib/tests/session_manager_test.php b/lib/tests/session_manager_test.php
index 34239875caa..9f025c7774d 100644
--- a/lib/tests/session_manager_test.php
+++ b/lib/tests/session_manager_test.php
@@ -99,15 +99,15 @@ class session_manager_test extends \advanced_testcase {
         $this->assertEquals(0, $USER->id);
 
         $user = $this->getDataGenerator()->create_user();
-        $this->assertObjectHasAttribute('description', $user);
-        $this->assertObjectHasAttribute('password', $user);
+        $this->assertObjectHasProperty('description', $user);
+        $this->assertObjectHasProperty('password', $user);
 
         \core\session\manager::set_user($user);
 
         $this->assertEquals($user->id, $USER->id);
-        $this->assertObjectNotHasAttribute('description', $user);
-        $this->assertObjectNotHasAttribute('password', $user);
-        $this->assertObjectHasAttribute('sesskey', $user);
+        $this->assertObjectNotHasProperty('description', $user);
+        $this->assertObjectNotHasProperty('password', $user);
+        $this->assertObjectHasProperty('sesskey', $user);
         $this->assertSame($user, $GLOBALS['USER']);
         $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
         $this->assertSame($GLOBALS['USER'], $USER);
@@ -124,8 +124,8 @@ class session_manager_test extends \advanced_testcase {
         @\core\session\manager::login_user($user); // Ignore header error messages.
         $this->assertEquals($user->id, $USER->id);
 
-        $this->assertObjectNotHasAttribute('description', $user);
-        $this->assertObjectNotHasAttribute('password', $user);
+        $this->assertObjectNotHasProperty('description', $user);
+        $this->assertObjectNotHasProperty('password', $user);
         $this->assertSame($user, $GLOBALS['USER']);
         $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
         $this->assertSame($GLOBALS['USER'], $USER);
@@ -558,7 +558,7 @@ class session_manager_test extends \advanced_testcase {
         $_SESSION['extra'] = true;
 
         // Try admin loginas this user in system context.
-        $this->assertObjectNotHasAttribute('realuser', $USER);
+        $this->assertObjectNotHasProperty('realuser', $USER);
         \core\session\manager::loginas($user->id, \context_system::instance());
 
         $this->assertSame($user->id, $USER->id);
diff --git a/lib/tests/sessionlib_test.php b/lib/tests/sessionlib_test.php
index f4e40ce08ae..aa801ee37d9 100644
--- a/lib/tests/sessionlib_test.php
+++ b/lib/tests/sessionlib_test.php
@@ -76,7 +76,7 @@ class sessionlib_test extends \advanced_testcase {
         $this->assertSame($user1->id, $USER->id);
         $this->assertSame($PAGE->context, \context_course::instance($SITE->id));
         $this->assertNotSame($adminsession, $SESSION);
-        $this->assertObjectNotHasAttribute('test1', $SESSION);
+        $this->assertObjectNotHasProperty('test1', $SESSION);
         $this->assertEmpty((array)$SESSION);
         $usersession1 = $SESSION;
         $SESSION->test2 = true;
@@ -261,7 +261,7 @@ class sessionlib_test extends \advanced_testcase {
         $user = $this->getDataGenerator()->create_user();
 
         \core\session\manager::init_empty_session();
-        $this->assertObjectNotHasAttribute('sesskey', $USER);
+        $this->assertObjectNotHasProperty('sesskey', $USER);
 
         $sesskey = sesskey();
         $this->assertNotEmpty($sesskey);
diff --git a/lib/tests/upgradelib_test.php b/lib/tests/upgradelib_test.php
index 4b56c8507c1..4da34eb8e9c 100644
--- a/lib/tests/upgradelib_test.php
+++ b/lib/tests/upgradelib_test.php
@@ -915,8 +915,8 @@ class upgradelib_test extends advanced_testcase {
 
         foreach ($licenses as $license) {
             $this->assertContains($license->shortname, $expectedshortnames);
-            $this->assertObjectHasAttribute('custom', $license);
-            $this->assertObjectHasAttribute('sortorder', $license);
+            $this->assertObjectHasProperty('custom', $license);
+            $this->assertObjectHasProperty('sortorder', $license);
         }
         // A core license which was deleted prior to upgrade should not be reinstalled.
         $actualshortnames = $DB->get_records_menu('license', null, '', 'id, shortname');
@@ -1568,7 +1568,7 @@ calendar,core_calendar|/calendar/view.php?view=month',
     public function test_moodle_start_upgrade_outageless() {
         global $CFG;
         $this->resetAfterTest();
-        $this->assertObjectNotHasAttribute('upgraderunning', $CFG);
+        $this->assertObjectNotHasProperty('upgraderunning', $CFG);
 
         // Confirm that starting normally sets the upgraderunning flag.
         upgrade_started();
@@ -1592,7 +1592,7 @@ calendar,core_calendar|/calendar/view.php?view=month',
     public function test_moodle_set_upgrade_timeout_outageless() {
         global $CFG;
         $this->resetAfterTest();
-        $this->assertObjectNotHasAttribute('upgraderunning', $CFG);
+        $this->assertObjectNotHasProperty('upgraderunning', $CFG);
 
         // Confirm running normally sets the timeout.
         upgrade_set_timeout(120);
diff --git a/lib/tests/user_test.php b/lib/tests/user_test.php
index 0a82f09f186..29b6de77eea 100644
--- a/lib/tests/user_test.php
+++ b/lib/tests/user_test.php
@@ -178,12 +178,12 @@ class user_test extends \advanced_testcase {
         $this->assertEquals('House', $result[0]->lastname);
         $this->assertEquals('house@x.x', $result[0]->email);
         $this->assertEquals(0, $result[0]->deleted);
-        $this->assertObjectHasAttribute('firstnamephonetic', $result[0]);
-        $this->assertObjectHasAttribute('lastnamephonetic', $result[0]);
-        $this->assertObjectHasAttribute('middlename', $result[0]);
-        $this->assertObjectHasAttribute('alternatename', $result[0]);
-        $this->assertObjectHasAttribute('imagealt', $result[0]);
-        $this->assertObjectHasAttribute('username', $result[0]);
+        $this->assertObjectHasProperty('firstnamephonetic', $result[0]);
+        $this->assertObjectHasProperty('lastnamephonetic', $result[0]);
+        $this->assertObjectHasProperty('middlename', $result[0]);
+        $this->assertObjectHasProperty('alternatename', $result[0]);
+        $this->assertObjectHasProperty('imagealt', $result[0]);
+        $this->assertObjectHasProperty('username', $result[0]);
 
         // Now search by lastname, both names, and partials, case-insensitive.
         $this->assertEquals($result, \core_user::search('House'));
diff --git a/lib/upgrade.txt b/lib/upgrade.txt
index 355f45b352e..163bde8bb81 100644
--- a/lib/upgrade.txt
+++ b/lib/upgrade.txt
@@ -100,6 +100,43 @@ information provided here is intended especially for developers.
 * core\hook\manager::phpunit_get_instance() now sets self::$instance to the mocked instance if the optional $persist argument is
   true, so future calls to ::get_instance() will return it.
 * The triggerSelector method in the `core/comboboxsearch/search_combobox` JS module is deprecated. It was not used.
+* PHPUnit has been upgraded to 9.6 (see MDL-81266 for details).
+  The main goal of the update is to allow developers to know in advance,
+  via deprecations, which stuff is going to stop working (because of being removed)
+  with PHPUnit 10 (see MDL-80969 for details). Other than that, the changes are minimal.
+  This is the list of noticeable changes:
+  - Deprecation: MDL-81281. A number of attribute-related assertions have been deprecated, will
+    be removed with PHPUnit 10. Alternatives for *some* of them are available:
+      - assertClassHasAttribute()
+      - assertClassNotHasAttribute()
+      - assertClassHasStaticAttribute()
+      - assertClassNotHasStaticAttribute()
+      - assertObjectHasAttribute()         => assertObjectHasProperty()
+      - assertObjectNotHasAttribute()      => assertObjectNotHasProperty()
+  - Deprecation: MDL-81266. A number of deprecation/notice/warning/error expectations have
+    been deprecated, will be removed with PHPUnit 10. No alternative exists. A working
+    replacement is available in the linked issue, hopefully there aren't many cases.
+      - expectDeprecation()
+      - expectDeprecationMessage()
+      - expectDeprecationMessageMatches()
+      - expectError()
+      - expectErrorMessage()
+      - expectErrorMessageMatches()
+      - expectNotice()
+      - expectNoticeMessage()
+      - expectNoticeMessageMatches()
+      - expectWarning()
+      - expectWarningMessage()
+      - expectWarningMessageMatches()
+  - Deprecation: MDL-81308. The ->withConsecutive() functionality on PHPUnit mocks
+    has been *silently* deprecated, will be removed with PHPUnit 10. Note that this
+    won't affect PHPUnit 9.6 runs and an alternative path will be
+    proposed in the linked issue, part of the PHPUnit 10 epic.
+  - Deprecation: PHPUnit\Framework\TestCase::getMockClass() has been deprecated, will
+    be removed with PHPUnit 10. No clear alternative exists and won't be investigated,
+    because there aren't cases in core.
+  - Deprecation: Cannot use the "Test" suffix on abstract test case classes. Proceed to
+    rename them to end with "TestCase" instead.
 
 === 4.3 ===
 
diff --git a/lib/xapi/tests/local/statement_test.php b/lib/xapi/tests/local/statement_test.php
index c9ff7be7aef..b60e02cf8a4 100644
--- a/lib/xapi/tests/local/statement_test.php
+++ b/lib/xapi/tests/local/statement_test.php
@@ -171,10 +171,10 @@ class statement_test extends advanced_testcase {
         $alldefined = array_merge($extras, $extravalues);
         foreach ($allextras as $extra) {
             if (in_array($extra, $alldefined)) {
-                $this->assertObjectHasAttribute($extra, $data);
+                $this->assertObjectHasProperty($extra, $data);
                 $this->assertNotEmpty($data->$extra);
             } else {
-                $this->assertObjectNotHasAttribute($extra, $data);
+                $this->assertObjectNotHasProperty($extra, $data);
             }
         }
     }
@@ -305,10 +305,10 @@ class statement_test extends advanced_testcase {
         $alldefined = array_merge($extras, $extravalues);
         foreach ($allextras as $extra) {
             if (in_array($extra, $alldefined)) {
-                $this->assertObjectHasAttribute($extra, $data);
+                $this->assertObjectHasProperty($extra, $data);
                 $this->assertNotEmpty($data->object);
             } else {
-                $this->assertObjectNotHasAttribute($extra, $data);
+                $this->assertObjectNotHasProperty($extra, $data);
             }
         }
     }
@@ -340,7 +340,7 @@ class statement_test extends advanced_testcase {
 
         // Check resulting json.
         $statementdata = json_decode(json_encode($statement));
-        $this->assertObjectHasAttribute('attachments', $statementdata);
+        $this->assertObjectHasProperty('attachments', $statementdata);
         $this->assertNotEmpty($statementdata->attachments);
         $this->assertCount(1, $statementdata->attachments);
     }
@@ -382,7 +382,7 @@ class statement_test extends advanced_testcase {
         $this->assertEquals($itemdata->length, $attachmentdata->length);
 
         $statementdata = json_decode(json_encode($statement));
-        $this->assertObjectHasAttribute('attachments', $statementdata);
+        $this->assertObjectHasProperty('attachments', $statementdata);
         $this->assertNotEmpty($statementdata->attachments);
         $this->assertCount(1, $statementdata->attachments);
 
diff --git a/message/output/popup/tests/externallib_test.php b/message/output/popup/tests/externallib_test.php
index 1b27e023057..a5e3988d7a2 100644
--- a/message/output/popup/tests/externallib_test.php
+++ b/message/output/popup/tests/externallib_test.php
@@ -101,7 +101,7 @@ class externallib_test extends \advanced_testcase {
         $found = 0;
         foreach ($result['notifications'] as $notification) {
             if (!empty($notification->customdata)) {
-                $this->assertObjectHasAttribute('datakey', json_decode($notification->customdata));
+                $this->assertObjectHasProperty('datakey', json_decode($notification->customdata));
                 $found++;
             }
         }
diff --git a/message/tests/api_test.php b/message/tests/api_test.php
index 1d6496e898b..c555a4a1218 100644
--- a/message/tests/api_test.php
+++ b/message/tests/api_test.php
@@ -1212,36 +1212,36 @@ class api_test extends messagelib_test {
 
         // Verify format of the return structure.
         foreach ($conversations as $conv) {
-            $this->assertObjectHasAttribute('id', $conv);
-            $this->assertObjectHasAttribute('name', $conv);
-            $this->assertObjectHasAttribute('subname', $conv);
-            $this->assertObjectHasAttribute('imageurl', $conv);
-            $this->assertObjectHasAttribute('type', $conv);
-            $this->assertObjectHasAttribute('isfavourite', $conv);
-            $this->assertObjectHasAttribute('membercount', $conv);
-            $this->assertObjectHasAttribute('isread', $conv);
-            $this->assertObjectHasAttribute('unreadcount', $conv);
-            $this->assertObjectHasAttribute('members', $conv);
+            $this->assertObjectHasProperty('id', $conv);
+            $this->assertObjectHasProperty('name', $conv);
+            $this->assertObjectHasProperty('subname', $conv);
+            $this->assertObjectHasProperty('imageurl', $conv);
+            $this->assertObjectHasProperty('type', $conv);
+            $this->assertObjectHasProperty('isfavourite', $conv);
+            $this->assertObjectHasProperty('membercount', $conv);
+            $this->assertObjectHasProperty('isread', $conv);
+            $this->assertObjectHasProperty('unreadcount', $conv);
+            $this->assertObjectHasProperty('members', $conv);
             foreach ($conv->members as $member) {
-                $this->assertObjectHasAttribute('id', $member);
-                $this->assertObjectHasAttribute('fullname', $member);
-                $this->assertObjectHasAttribute('profileimageurl', $member);
-                $this->assertObjectHasAttribute('profileimageurlsmall', $member);
-                $this->assertObjectHasAttribute('isonline', $member);
-                $this->assertObjectHasAttribute('showonlinestatus', $member);
-                $this->assertObjectHasAttribute('isblocked', $member);
-                $this->assertObjectHasAttribute('iscontact', $member);
-                $this->assertObjectHasAttribute('isdeleted', $member);
-                $this->assertObjectHasAttribute('canmessage', $member);
-                $this->assertObjectHasAttribute('requirescontact', $member);
-                $this->assertObjectHasAttribute('contactrequests', $member);
+                $this->assertObjectHasProperty('id', $member);
+                $this->assertObjectHasProperty('fullname', $member);
+                $this->assertObjectHasProperty('profileimageurl', $member);
+                $this->assertObjectHasProperty('profileimageurlsmall', $member);
+                $this->assertObjectHasProperty('isonline', $member);
+                $this->assertObjectHasProperty('showonlinestatus', $member);
+                $this->assertObjectHasProperty('isblocked', $member);
+                $this->assertObjectHasProperty('iscontact', $member);
+                $this->assertObjectHasProperty('isdeleted', $member);
+                $this->assertObjectHasProperty('canmessage', $member);
+                $this->assertObjectHasProperty('requirescontact', $member);
+                $this->assertObjectHasProperty('contactrequests', $member);
             }
-            $this->assertObjectHasAttribute('messages', $conv);
+            $this->assertObjectHasProperty('messages', $conv);
             foreach ($conv->messages as $message) {
-                $this->assertObjectHasAttribute('id', $message);
-                $this->assertObjectHasAttribute('useridfrom', $message);
-                $this->assertObjectHasAttribute('text', $message);
-                $this->assertObjectHasAttribute('timecreated', $message);
+                $this->assertObjectHasProperty('id', $message);
+                $this->assertObjectHasProperty('useridfrom', $message);
+                $this->assertObjectHasProperty('text', $message);
+                $this->assertObjectHasProperty('timecreated', $message);
             }
         }
     }
@@ -4232,8 +4232,8 @@ class api_test extends messagelib_test {
         $sink->close();
         // Test customdata.
         $customdata = json_decode($messages[0]->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
-        $this->assertObjectHasAttribute('actionbuttons', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('actionbuttons', $customdata);
         $this->assertCount(2, (array) $customdata->actionbuttons);
 
         $this->assertEquals($user1->id, $request->userid);
@@ -4308,12 +4308,12 @@ class api_test extends messagelib_test {
 
         $this->assertEquals($user2->id, $request->id);
         $this->assertEquals(fullname($user2), $request->fullname);
-        $this->assertObjectHasAttribute('profileimageurl', $request);
-        $this->assertObjectHasAttribute('profileimageurlsmall', $request);
-        $this->assertObjectHasAttribute('isonline', $request);
-        $this->assertObjectHasAttribute('showonlinestatus', $request);
-        $this->assertObjectHasAttribute('isblocked', $request);
-        $this->assertObjectHasAttribute('iscontact', $request);
+        $this->assertObjectHasProperty('profileimageurl', $request);
+        $this->assertObjectHasProperty('profileimageurlsmall', $request);
+        $this->assertObjectHasProperty('isonline', $request);
+        $this->assertObjectHasProperty('showonlinestatus', $request);
+        $this->assertObjectHasProperty('isblocked', $request);
+        $this->assertObjectHasProperty('iscontact', $request);
     }
 
     /**
@@ -4980,7 +4980,7 @@ class api_test extends messagelib_test {
         $this->assertEquals(true, $member1->showonlinestatus);
         $this->assertEquals(false, $member1->iscontact);
         $this->assertEquals(false, $member1->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member1);
+        $this->assertObjectHasProperty('contactrequests', $member1);
         $this->assertEmpty($member1->contactrequests);
 
         $this->assertEquals($user2->id, $member2->id);
@@ -4989,7 +4989,7 @@ class api_test extends messagelib_test {
         $this->assertEquals(true, $member2->showonlinestatus);
         $this->assertEquals(true, $member2->iscontact);
         $this->assertEquals(false, $member2->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member2);
+        $this->assertObjectHasProperty('contactrequests', $member2);
         $this->assertEmpty($member2->contactrequests);
 
         $this->assertEquals($user3->id, $member3->id);
@@ -4998,7 +4998,7 @@ class api_test extends messagelib_test {
         $this->assertEquals(true, $member3->showonlinestatus);
         $this->assertEquals(false, $member3->iscontact);
         $this->assertEquals(true, $member3->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member3);
+        $this->assertObjectHasProperty('contactrequests', $member3);
         $this->assertEmpty($member3->contactrequests);
     }
 
@@ -5149,18 +5149,18 @@ class api_test extends messagelib_test {
         $messages = $messagessink->get_messages();
         // Test customdata.
         $customdata = json_decode($messages[0]->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
-        $this->assertObjectHasAttribute('actionbuttons', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('actionbuttons', $customdata);
         $this->assertCount(1, (array) $customdata->actionbuttons);
-        $this->assertObjectHasAttribute('placeholders', $customdata);
+        $this->assertObjectHasProperty('placeholders', $customdata);
         $this->assertCount(1, (array) $customdata->placeholders);
 
         // Verify the message returned.
         $this->assertInstanceOf(\stdClass::class, $message1);
-        $this->assertObjectHasAttribute('id', $message1);
+        $this->assertObjectHasProperty('id', $message1);
         $this->assertEquals($user1->id, $message1->useridfrom);
         $this->assertEquals('this is a message', $message1->text);
-        $this->assertObjectHasAttribute('timecreated', $message1);
+        $this->assertObjectHasProperty('timecreated', $message1);
 
         // Verify events. Note: the event is a message read event because of an if (PHPUNIT) conditional within message_send(),
         // however, we can still determine the number and ids of any recipients this way.
@@ -5199,17 +5199,17 @@ class api_test extends messagelib_test {
         $messages = $messagessink->get_messages();
         // Verify the message returned.
         $this->assertInstanceOf(\stdClass::class, $message1);
-        $this->assertObjectHasAttribute('id', $message1);
+        $this->assertObjectHasProperty('id', $message1);
         $this->assertEquals($user1->id, $message1->useridfrom);
         $this->assertEquals('message to the group', $message1->text);
-        $this->assertObjectHasAttribute('timecreated', $message1);
+        $this->assertObjectHasProperty('timecreated', $message1);
         // Test customdata.
         $customdata = json_decode($messages[0]->customdata);
-        $this->assertObjectHasAttribute('actionbuttons', $customdata);
+        $this->assertObjectHasProperty('actionbuttons', $customdata);
         $this->assertCount(1, (array) $customdata->actionbuttons);
-        $this->assertObjectHasAttribute('placeholders', $customdata);
+        $this->assertObjectHasProperty('placeholders', $customdata);
         $this->assertCount(1, (array) $customdata->placeholders);
-        $this->assertObjectNotHasAttribute('notificationiconurl', $customdata);    // No group image means no image.
+        $this->assertObjectNotHasProperty('notificationiconurl', $customdata);    // No group image means no image.
 
         // Verify events. Note: the event is a message read event because of an if (PHPUNIT) conditional within message_send(),
         // however, we can still determine the number and ids of any recipients this way.
@@ -5268,14 +5268,14 @@ class api_test extends messagelib_test {
         $messages = $messagessink->get_messages();
         // Verify the message returned.
         $this->assertInstanceOf(\stdClass::class, $message1);
-        $this->assertObjectHasAttribute('id', $message1);
+        $this->assertObjectHasProperty('id', $message1);
         $this->assertEquals($user1->id, $message1->useridfrom);
         $this->assertEquals('message to the group', $message1->text);
-        $this->assertObjectHasAttribute('timecreated', $message1);
+        $this->assertObjectHasProperty('timecreated', $message1);
         // Test customdata.
         $customdata = json_decode($messages[0]->customdata);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
-        $this->assertObjectHasAttribute('notificationsendericonurl', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('notificationsendericonurl', $customdata);
         $this->assertEquals($groupimageurl, $customdata->notificationiconurl);
         $this->assertEquals($group->name, $customdata->conversationname);
         $userpicture = new \user_picture($user1);
diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php
index e1a54b81351..02538447d3a 100644
--- a/message/tests/externallib_test.php
+++ b/message/tests/externallib_test.php
@@ -1484,7 +1484,7 @@ class externallib_test extends externallib_advanced_testcase {
         $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
         $this->assertCount(1, $messages['messages']);
         // Check we receive custom data as a unserialisable json.
-        $this->assertObjectHasAttribute('datakey', json_decode($messages['messages'][0]['customdata']));
+        $this->assertObjectHasProperty('datakey', json_decode($messages['messages'][0]['customdata']));
         $this->assertEquals('mod_feedback', $messages['messages'][0]['component']);
         $this->assertEquals('submission', $messages['messages'][0]['eventtype']);
         $feedbackicon = clean_param($PAGE->get_renderer('core')->image_url('monologo', 'mod_feedback')->out(), PARAM_URL);
@@ -4527,7 +4527,7 @@ class externallib_test extends externallib_advanced_testcase {
         $this->assertEquals(true, $member1->showonlinestatus);
         $this->assertEquals(false, $member1->iscontact);
         $this->assertEquals(false, $member1->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member1);
+        $this->assertObjectHasProperty('contactrequests', $member1);
         $this->assertEmpty($member1->contactrequests);
 
         $this->assertEquals($user2->id, $member2->id);
@@ -4536,7 +4536,7 @@ class externallib_test extends externallib_advanced_testcase {
         $this->assertEquals(true, $member2->showonlinestatus);
         $this->assertEquals(true, $member2->iscontact);
         $this->assertEquals(false, $member2->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member2);
+        $this->assertObjectHasProperty('contactrequests', $member2);
         $this->assertEmpty($member2->contactrequests);
 
         $this->assertEquals($user3->id, $member3->id);
@@ -4545,7 +4545,7 @@ class externallib_test extends externallib_advanced_testcase {
         $this->assertEquals(true, $member3->showonlinestatus);
         $this->assertEquals(false, $member3->iscontact);
         $this->assertEquals(true, $member3->isblocked);
-        $this->assertObjectHasAttribute('contactrequests', $member3);
+        $this->assertObjectHasProperty('contactrequests', $member3);
         $this->assertEmpty($member3->contactrequests);
     }
 
@@ -4712,12 +4712,12 @@ class externallib_test extends externallib_advanced_testcase {
         external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
 
         $this->assertCount(2, $writtenmessages);
-        $this->assertObjectHasAttribute('id', $writtenmessages[0]);
+        $this->assertObjectHasProperty('id', $writtenmessages[0]);
         $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
         $this->assertEquals('<p>a message from user 1</p>', $writtenmessages[0]->text);
         $this->assertNotEmpty($writtenmessages[0]->timecreated);
 
-        $this->assertObjectHasAttribute('id', $writtenmessages[1]);
+        $this->assertObjectHasProperty('id', $writtenmessages[1]);
         $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
         $this->assertEquals('<p>another message from user 1</p>', $writtenmessages[1]->text);
         $this->assertNotEmpty($writtenmessages[1]->timecreated);
@@ -4764,12 +4764,12 @@ class externallib_test extends externallib_advanced_testcase {
         external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
 
         $this->assertCount(2, $writtenmessages);
-        $this->assertObjectHasAttribute('id', $writtenmessages[0]);
+        $this->assertObjectHasProperty('id', $writtenmessages[0]);
         $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
         $this->assertEquals('<p>a message from user 1 to group conv</p>', $writtenmessages[0]->text);
         $this->assertNotEmpty($writtenmessages[0]->timecreated);
 
-        $this->assertObjectHasAttribute('id', $writtenmessages[1]);
+        $this->assertObjectHasProperty('id', $writtenmessages[1]);
         $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
         $this->assertEquals('<p>another message from user 1 to group conv</p>', $writtenmessages[1]->text);
         $this->assertNotEmpty($writtenmessages[1]->timecreated);
diff --git a/mod/feedback/tests/external/external_test.php b/mod/feedback/tests/external/external_test.php
index 1ea702e6e50..018a887709b 100644
--- a/mod/feedback/tests/external/external_test.php
+++ b/mod/feedback/tests/external/external_test.php
@@ -759,7 +759,7 @@ class external_test extends externallib_advanced_testcase {
         $customdata = json_decode($messages[0]->customdata);
         $this->assertEquals($this->feedback->id, $customdata->instance);
         $this->assertEquals($this->feedback->cmid, $customdata->cmid);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
     }
 
     /**
diff --git a/mod/forum/tests/mail_test.php b/mod/forum/tests/mail_test.php
index d1bfe8af0c3..e3e2ec820c9 100644
--- a/mod/forum/tests/mail_test.php
+++ b/mod/forum/tests/mail_test.php
@@ -1606,8 +1606,8 @@ class mail_test extends \advanced_testcase {
         $this->assertEquals($forum->cmid, $customdata->cmid);
         $this->assertEquals($post->id, $customdata->postid);
         $this->assertEquals($discussion->id, $customdata->discussionid);
-        $this->assertObjectHasAttribute('notificationiconurl', $customdata);
-        $this->assertObjectHasAttribute('actionbuttons', $customdata);
+        $this->assertObjectHasProperty('notificationiconurl', $customdata);
+        $this->assertObjectHasProperty('actionbuttons', $customdata);
         $this->assertCount(1, (array) $customdata->actionbuttons);
     }
 }
diff --git a/mod/lti/tests/locallib_test.php b/mod/lti/tests/locallib_test.php
index 48a9f58fa22..0f94476e7a3 100644
--- a/mod/lti/tests/locallib_test.php
+++ b/mod/lti/tests/locallib_test.php
@@ -302,21 +302,21 @@ class locallib_test extends mod_lti_testcase {
 
         // Try when the forcessl config property is not set.
         lti_prepare_type_for_save($type, $config);
-        $this->assertObjectHasAttribute('lti_forcessl', $config);
+        $this->assertObjectHasProperty('lti_forcessl', $config);
         $this->assertEquals(0, $config->lti_forcessl);
         $this->assertEquals(0, $type->forcessl);
 
         // Try when forcessl config property is set.
         $config->lti_forcessl = 1;
         lti_prepare_type_for_save($type, $config);
-        $this->assertObjectHasAttribute('lti_forcessl', $config);
+        $this->assertObjectHasProperty('lti_forcessl', $config);
         $this->assertEquals(1, $config->lti_forcessl);
         $this->assertEquals(1, $type->forcessl);
 
         // Try when forcessl config property is set to 0.
         $config->lti_forcessl = 0;
         lti_prepare_type_for_save($type, $config);
-        $this->assertObjectHasAttribute('lti_forcessl', $config);
+        $this->assertObjectHasProperty('lti_forcessl', $config);
         $this->assertEquals(0, $config->lti_forcessl);
         $this->assertEquals(0, $type->forcessl);
     }
diff --git a/mod/quiz/tests/external/external_test.php b/mod/quiz/tests/external/external_test.php
index 9824e71e160..0822c78906f 100644
--- a/mod/quiz/tests/external/external_test.php
+++ b/mod/quiz/tests/external/external_test.php
@@ -1553,7 +1553,7 @@ class external_test extends externallib_advanced_testcase {
             $this->assertEquals($quizobj->get_quizid(), $customdata->instance);
             $this->assertEquals($quizobj->get_cmid(), $customdata->cmid);
             $this->assertEquals($attempt->id, $customdata->attemptid);
-            $this->assertObjectHasAttribute('notificationiconurl', $customdata);
+            $this->assertObjectHasProperty('notificationiconurl', $customdata);
         }
 
         // Start new attempt.
diff --git a/mod/workshop/tests/privacy/provider_test.php b/mod/workshop/tests/privacy/provider_test.php
index a33272fab4e..7f167b29a7d 100644
--- a/mod/workshop/tests/privacy/provider_test.php
+++ b/mod/workshop/tests/privacy/provider_test.php
@@ -269,7 +269,7 @@ class provider_test extends provider_testcase {
 
         $workshop = $writer->get_data([]);
         $this->assertEquals('Workshop11', $workshop->name);
-        $this->assertObjectHasAttribute('phase', $workshop);
+        $this->assertObjectHasProperty('phase', $workshop);
 
         $mysubmission = $writer->get_data([
             get_string('mysubmission', 'mod_workshop'),
diff --git a/question/bank/columnsortorder/tests/column_manager_test.php b/question/bank/columnsortorder/tests/column_manager_test.php
index bff2d5df975..cdcb8685cd9 100644
--- a/question/bank/columnsortorder/tests/column_manager_test.php
+++ b/question/bank/columnsortorder/tests/column_manager_test.php
@@ -226,9 +226,9 @@ class column_manager_test extends advanced_testcase {
         $questionlistcolumns = $columnmanager->get_columns();
         $this->assertIsArray($questionlistcolumns);
         foreach ($questionlistcolumns as $columnnobject) {
-            $this->assertObjectHasAttribute('class', $columnnobject);
-            $this->assertObjectHasAttribute('name', $columnnobject);
-            $this->assertObjectHasAttribute('colname', $columnnobject);
+            $this->assertObjectHasProperty('class', $columnnobject);
+            $this->assertObjectHasProperty('name', $columnnobject);
+            $this->assertObjectHasProperty('colname', $columnnobject);
         }
     }
 
diff --git a/question/tests/externallib_test.php b/question/tests/externallib_test.php
index 7776f84d0bc..c34ddc2d857 100644
--- a/question/tests/externallib_test.php
+++ b/question/tests/externallib_test.php
@@ -356,10 +356,10 @@ class externallib_test extends externallib_advanced_testcase {
         $this->assertEquals($expected->qtype, $actual->qtype);
         // These values are added by the formatting. It doesn't matter what the
         // exact values are just that they are returned.
-        $this->assertObjectHasAttribute('icon', $actual);
-        $this->assertObjectHasAttribute('key', $actual->icon);
-        $this->assertObjectHasAttribute('component', $actual->icon);
-        $this->assertObjectHasAttribute('alttext', $actual->icon);
+        $this->assertObjectHasProperty('icon', $actual);
+        $this->assertObjectHasProperty('key', $actual->icon);
+        $this->assertObjectHasProperty('component', $actual->icon);
+        $this->assertObjectHasProperty('alttext', $actual->icon);
     }
 
     /**
diff --git a/question/type/match/tests/question_type_test.php b/question/type/match/tests/question_type_test.php
index da3b51514cd..792011f21ef 100644
--- a/question/type/match/tests/question_type_test.php
+++ b/question/type/match/tests/question_type_test.php
@@ -201,7 +201,7 @@ class question_type_test extends \advanced_testcase {
             }
         }
 
-        $this->assertObjectHasAttribute('subquestions', $actualquestiondata->options);
+        $this->assertObjectHasProperty('subquestions', $actualquestiondata->options);
 
         $subqpropstoignore = array('id');
         foreach ($questiondata->options->subquestions as $subq) {
diff --git a/question/type/multianswer/tests/question_type_test.php b/question/type/multianswer/tests/question_type_test.php
index fe36a8b0a3a..49a924b4f94 100644
--- a/question/type/multianswer/tests/question_type_test.php
+++ b/question/type/multianswer/tests/question_type_test.php
@@ -313,7 +313,7 @@ class question_type_test extends \advanced_testcase {
             }
         }
 
-        $this->assertObjectHasAttribute('questions', $actualquestiondata->options);
+        $this->assertObjectHasProperty('questions', $actualquestiondata->options);
 
         $subqpropstoignore =
             ['id', 'category', 'parent', 'contextid', 'question', 'options', 'stamp', 'timemodified',
diff --git a/reportbuilder/tests/external/custom_report_details_exporter_test.php b/reportbuilder/tests/external/custom_report_details_exporter_test.php
index 25b74f34a0f..4e143003e8b 100644
--- a/reportbuilder/tests/external/custom_report_details_exporter_test.php
+++ b/reportbuilder/tests/external/custom_report_details_exporter_test.php
@@ -58,7 +58,7 @@ class custom_report_details_exporter_test extends advanced_testcase {
         $this->assertEquals(users::get_name(), $export->sourcename);
 
         // We use the user exporter for the modifier of the report.
-        $this->assertObjectHasAttribute('modifiedby', $export);
+        $this->assertObjectHasProperty('modifiedby', $export);
         $this->assertEquals(fullname($user), $export->modifiedby->fullname);
     }
 }
diff --git a/reportbuilder/tests/external/custom_report_exporter_test.php b/reportbuilder/tests/external/custom_report_exporter_test.php
index 297db31ddb0..6256826a7dd 100644
--- a/reportbuilder/tests/external/custom_report_exporter_test.php
+++ b/reportbuilder/tests/external/custom_report_exporter_test.php
@@ -98,11 +98,11 @@ class custom_report_exporter_test extends advanced_testcase {
         ], $export->attributes);
 
         // The following are all generated by additional exporters, and should not be present when not editing.
-        $this->assertObjectNotHasAttribute('sidebarmenucards', $export);
-        $this->assertObjectNotHasAttribute('conditions', $export);
-        $this->assertObjectNotHasAttribute('filters', $export);
-        $this->assertObjectNotHasAttribute('sorting', $export);
-        $this->assertObjectNotHasAttribute('cardview', $export);
+        $this->assertObjectNotHasProperty('sidebarmenucards', $export);
+        $this->assertObjectNotHasProperty('conditions', $export);
+        $this->assertObjectNotHasProperty('filters', $export);
+        $this->assertObjectNotHasProperty('sorting', $export);
+        $this->assertObjectNotHasProperty('cardview', $export);
     }
 
     /**
diff --git a/user/tests/fields_test.php b/user/tests/fields_test.php
index 1d7977be335..2fd0b2e2a0b 100644
--- a/user/tests/fields_test.php
+++ b/user/tests/fields_test.php
@@ -391,12 +391,12 @@ class fields_test extends \advanced_testcase {
         $this->assertCount(2, $records);
 
         // User id was renamed.
-        $this->assertObjectNotHasAttribute('id', $records['XXX1']);
-        $this->assertObjectHasAttribute('userid', $records['XXX1']);
+        $this->assertObjectNotHasProperty('id', $records['XXX1']);
+        $this->assertObjectHasProperty('userid', $records['XXX1']);
 
         // Other fields are normal (just try a couple).
-        $this->assertObjectHasAttribute('firstname', $records['XXX1']);
-        $this->assertObjectHasAttribute('imagealt', $records['XXX1']);
+        $this->assertObjectHasProperty('firstname', $records['XXX1']);
+        $this->assertObjectHasProperty('imagealt', $records['XXX1']);
 
         // Check the user id is actually right.
         $this->assertEquals('XXX1',
@@ -415,13 +415,13 @@ class fields_test extends \advanced_testcase {
         $this->assertCount(2, $records);
 
         // User id was renamed.
-        $this->assertObjectNotHasAttribute('id', $records['XXX1']);
-        $this->assertObjectNotHasAttribute('u_id', $records['XXX1']);
-        $this->assertObjectHasAttribute('userid', $records['XXX1']);
+        $this->assertObjectNotHasProperty('id', $records['XXX1']);
+        $this->assertObjectNotHasProperty('u_id', $records['XXX1']);
+        $this->assertObjectHasProperty('userid', $records['XXX1']);
 
         // Other fields are prefixed (just try a couple).
-        $this->assertObjectHasAttribute('u_firstname', $records['XXX1']);
-        $this->assertObjectHasAttribute('u_imagealt', $records['XXX1']);
+        $this->assertObjectHasProperty('u_firstname', $records['XXX1']);
+        $this->assertObjectHasProperty('u_imagealt', $records['XXX1']);
 
         // Without a leading comma.
         ['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
@@ -437,8 +437,8 @@ class fields_test extends \advanced_testcase {
             // ID should be the first field used by get_records_sql.
             $this->assertEquals($key, $record->id);
             // Check 2 other sample properties.
-            $this->assertObjectHasAttribute('firstname', $record);
-            $this->assertObjectHasAttribute('imagealt', $record);
+            $this->assertObjectHasProperty('firstname', $record);
+            $this->assertObjectHasProperty('imagealt', $record);
         }
     }
 
diff --git a/user/tests/profilelib_test.php b/user/tests/profilelib_test.php
index e8609d71056..3daa968211d 100644
--- a/user/tests/profilelib_test.php
+++ b/user/tests/profilelib_test.php
@@ -56,10 +56,10 @@ class profilelib_test extends \advanced_testcase {
         $this->assertArrayNotHasKey($id1, profile_get_custom_fields(true));
 
         // Check that profile_user_record returns same (no) fields.
-        $this->assertObjectNotHasAttribute('frogdesc', profile_user_record($user->id));
+        $this->assertObjectNotHasProperty('frogdesc', profile_user_record($user->id));
 
         // Check that profile_user_record returns all the fields when requested.
-        $this->assertObjectHasAttribute('frogdesc', profile_user_record($user->id, false));
+        $this->assertObjectHasProperty('frogdesc', profile_user_record($user->id, false));
 
         // Add another custom field, this time of normal text type.
         $id2 = $this->getDataGenerator()->create_custom_profile_field(array(
@@ -75,10 +75,10 @@ class profilelib_test extends \advanced_testcase {
         $this->assertArrayHasKey($id2, profile_get_custom_fields(true));
 
         // Check profile_user_record returns same field.
-        $this->assertObjectHasAttribute('frogname', profile_user_record($user->id));
+        $this->assertObjectHasProperty('frogname', profile_user_record($user->id));
 
         // Check that profile_user_record returns all the fields when requested.
-        $this->assertObjectHasAttribute('frogname', profile_user_record($user->id, false));
+        $this->assertObjectHasProperty('frogname', profile_user_record($user->id, false));
     }
 
     /**
@@ -233,7 +233,7 @@ class profilelib_test extends \advanced_testcase {
         $this->assertEquals('Gryffindor', $profilefields1->house);
 
         $profilefields2 = profile_user_record($harry->id);
-        $this->assertObjectHasAttribute('house', $profilefields2);
+        $this->assertObjectHasProperty('house', $profilefields2);
         $this->assertNull($profilefields2->house);
     }
 
diff --git a/user/tests/userselector_test.php b/user/tests/userselector_test.php
index 2cdf60d7714..78b8f261955 100644
--- a/user/tests/userselector_test.php
+++ b/user/tests/userselector_test.php
@@ -75,9 +75,9 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($selector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectNotHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectNotHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
     }
@@ -95,17 +95,17 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($systemselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectNotHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectNotHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
 
         foreach ($courseselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
             }
         }
     }
@@ -124,9 +124,9 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($courseselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
     }
@@ -144,17 +144,17 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($systemselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
             }
         }
 
         foreach ($courseselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
             }
         }
     }
@@ -178,17 +178,17 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($systemselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
 
         foreach ($courseselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
             }
         }
     }
@@ -209,17 +209,17 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($systemselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
 
         foreach ($courseselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectNotHasAttribute('country', $user);
-                $this->assertObjectNotHasAttribute('city', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectNotHasProperty('country', $user);
+                $this->assertObjectNotHasProperty('city', $user);
             }
         }
     }
@@ -242,21 +242,21 @@ class userselector_test extends \advanced_testcase {
 
         foreach ($implicitselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
-                $this->assertObjectNotHasAttribute('email', $user);
-                $this->assertObjectNotHasAttribute('department', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
+                $this->assertObjectNotHasProperty('email', $user);
+                $this->assertObjectNotHasProperty('department', $user);
             }
         }
 
         foreach ($explicitselector->find_users('') as $found) {
             foreach ($found as $user) {
-                $this->assertObjectHasAttribute('idnumber', $user);
-                $this->assertObjectHasAttribute('country', $user);
-                $this->assertObjectHasAttribute('city', $user);
-                $this->assertObjectNotHasAttribute('email', $user);
-                $this->assertObjectNotHasAttribute('department', $user);
+                $this->assertObjectHasProperty('idnumber', $user);
+                $this->assertObjectHasProperty('country', $user);
+                $this->assertObjectHasProperty('city', $user);
+                $this->assertObjectNotHasProperty('email', $user);
+                $this->assertObjectNotHasProperty('department', $user);
             }
         }
     }