diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index c21b98049..d9b2164bd 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -40,6 +40,16 @@ jobs: extensions: intl, mbstring, mysqli, gd, bcmath, zip coverage: none + - name: Verify PHP patch level + run: | + MIN="8.2.35" + ACTUAL=$(php -r 'echo PHP_VERSION;') + if [ "$(printf '%s\n' "$MIN" "$ACTUAL" | sort -V | head -1)" != "$MIN" ]; then + echo "::error::PHP $ACTUAL is below patched minimum $MIN (CVE-2026-109618 / CVE-2026-109619)" + exit 1 + fi + echo "PHP $ACTUAL meets minimum $MIN" + - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -94,6 +104,16 @@ jobs: env: GITHUB_TAG: ${{ github.ref_name }} + - name: Normalize version for official release + if: startsWith(github.ref, 'refs/tags/') + run: | + VERSION="${{ steps.version.outputs.version }}" + # The tagged commit may still carry a -dev suffix in source files. + # Strip it so the archive contents report the clean release version. + sed -i "s/application_version = '${VERSION}-dev'/application_version = '${VERSION}'/" app/Config/App.php + sed -i "s/\"version\": \"${VERSION}-dev\"/,\"version\": \"${VERSION}\",/" package.json 2>/dev/null || true + echo "App.php now reports: $(grep application_version app/Config/App.php)" + - name: Validate release tag if: startsWith(github.ref, 'refs/tags/') run: | diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4d823dc3f..4ecdc4753 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,6 +43,22 @@ jobs: extensions: tokenizer coverage: none + - name: Verify PHP patch level + run: | + ACTUAL=$(php -r 'echo PHP_VERSION;') + case "${ACTUAL%.*}" in + 8.2) MIN="8.2.35" ;; + 8.3) MIN="8.3.36" ;; + 8.4) MIN="8.4.27" ;; + 8.5) MIN="8.5.12" ;; + *) echo "::error::Unsupported PHP branch: $ACTUAL"; exit 1 ;; + esac + if [ "$(printf '%s\n' "$MIN" "$ACTUAL" | sort -V | head -1)" != "$MIN" ]; then + echo "::error::PHP $ACTUAL is below patched minimum $MIN (CVE-2026-109618 / CVE-2026-109619)" + exit 1 + fi + echo "PHP $ACTUAL meets minimum $MIN" + - name: Get composer cache directory run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV diff --git a/.github/workflows/php-linter.yml b/.github/workflows/php-linter.yml index fb94c5b86..e31d40304 100644 --- a/.github/workflows/php-linter.yml +++ b/.github/workflows/php-linter.yml @@ -1,6 +1,6 @@ name: PHP Linting -on: push +on: [push, pull_request] jobs: phplint: diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 125c30be6..b8fee39ea 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -56,6 +56,22 @@ jobs: extensions: intl, mbstring, mysqli coverage: none + - name: Verify PHP patch level + run: | + ACTUAL=$(php -r 'echo PHP_VERSION;') + case "${ACTUAL%.*}" in + 8.2) MIN="8.2.35" ;; + 8.3) MIN="8.3.36" ;; + 8.4) MIN="8.4.27" ;; + 8.5) MIN="8.5.12" ;; + *) echo "::error::Unsupported PHP branch: $ACTUAL"; exit 1 ;; + esac + if [ "$(printf '%s\n' "$MIN" "$ACTUAL" | sort -V | head -1)" != "$MIN" ]; then + echo "::error::PHP $ACTUAL is below patched minimum $MIN (CVE-2026-109618 / CVE-2026-109619)" + exit 1 + fi + echo "PHP $ACTUAL meets minimum $MIN" + - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/AGENTS.md b/AGENTS.md index a35dc7595..d206b9ee9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,6 +28,52 @@ This document provides guidance for AI agents working on the Open Source Point o - Tests must pass before submitting changes - **One test file per class under test.** A controller, model, library, or helper gets exactly one test file covering all of its behavior — `Item_kits.php` → `tests/Controllers/Item_kitsTest.php` (or `Item_kitsControllerTest.php`, matching this codebase's existing `*ControllerTest.php` suffix for controllers), `Sale_lib.php` → `tests/Libraries/Sale_libTest.php`, etc. Do not create feature- or endpoint-scoped test files alongside a class's main test file (e.g. no `Item_kitsBarcodeTest.php` next to `Item_kitsControllerTest.php`) — add the new test methods to the existing file for that class instead. If no test file exists yet for the class, create the one canonical file rather than a narrowly-scoped one. - **Name test methods as `test{MethodUnderTest}_{Behavior}`.** `PascalCase` the method under test, then an underscore, then a short behavior phrase — `testPostSave_RejectsNegativeTaxPercent`, `testSaveLocale_AcceptsValidReferenceCodeMinMax`. Start the behavior with a verb: `Accepts`/`Requires` for valid or boundary cases, `Rejects` for invalid ones. The name should read as a self-documenting sentence and needs no docblock. +- **Resetting the test database (full migration reset, not just `$refresh`).** When a test class needs the `ospos_tests` schema fully dropped and re-migrated from scratch (e.g. so `ospos_permissions`/`app_config` get freshly seeded), `use Tests\Support\DatabaseBootstrapTrait;` and call `$this->resetDatabaseOnce()` as the first line of `setUp()`, before `parent::setUp()`: + ```php + use Tests\Support\DatabaseBootstrapTrait; + + class FooTest extends CIUnitTestCase + { + use DatabaseTestTrait; + use DatabaseBootstrapTrait; + + protected $migrate = true; + protected $migrateOnce = true; + protected $seedOnce = true; + protected $refresh = false; + protected $namespace = null; + + protected function setUp(): void + { + $this->resetDatabaseOnce(); + + parent::setUp(); + // ... + } + } + ``` + `DatabaseBootstrapTrait` (`tests/Support/DatabaseBootstrapTrait.php`) wraps `TestDatabaseBootstrapSeeder::reset()` with a per-class static guard so it only runs once per test class. Do **not** reinvent this with a local `private static bool $doneBootstrap` flag, `setUpBeforeClass()`, or `Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder')` followed by a manual `Config::connect($this->DBGroup)->close()`/`resetDataCache()` in the test file — those are the old patterns the trait replaces, and the manual version reintroduces the exact stale-connection bug `TestDatabaseBootstrapSeeder::reset()` already fixes internally. Only add this trait to test classes that actually touch the real test database (`DatabaseTestTrait` users) — pure unit tests that mock everything don't need it. + If a test class reads `config(OSPOS::class)->settings` for real (not mocking it out), also call `config(OSPOS::class)->update_settings();` right after `parent::setUp();` to force a fresh settings read post-reset. See `tests/Controllers/ReportsControllerTest.php` for that shape. +- **Mocking OSPOS config settings.** When a test needs `config(OSPOS::class)->settings` to return fixed values instead of reading from the database, `use Tests\Support\OsposConfigFixtureTrait;` and call `$this->injectOsposSettings([...])` instead of manually constructing `new OSPOS()` + `Factories::injectMock('config', OSPOS::class, $ospos)`: + ```php + use Tests\Support\OsposConfigFixtureTrait; + + class FooTest extends CIUnitTestCase + { + use OsposConfigFixtureTrait; + + protected function setUp(): void + { + parent::setUp(); + + $this->injectOsposSettings([ + 'number_locale' => 'en_US', + 'currency_decimals' => 2, + ]); + } + } + ``` + `OSPOS::getDefaultSettings()` is `private` and only covers a handful of keys, so it is not a usable base to merge onto — each test must supply the exact settings it depends on. Use this trait regardless of whether the test also uses `DatabaseBootstrapTrait`. ## Build diff --git a/CHANGELOG.md b/CHANGELOG.md index 890a137b2..fd5505d94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ -[unreleased]: https://github.com/opensourcepos/opensourcepos/compare/3.4.2...HEAD +[unreleased]: https://github.com/opensourcepos/opensourcepos/compare/3.4.3...HEAD +[3.4.3]: https://github.com/opensourcepos/opensourcepos/compare/3.4.2...3.4.3 [3.4.2]: https://github.com/opensourcepos/opensourcepos/compare/3.4.1...3.4.2 [3.4.1]: https://github.com/opensourcepos/opensourcepos/compare/3.4.0...3.4.1 [3.4.0]: https://github.com/opensourcepos/opensourcepos/compare/3.3.9...3.4.0 @@ -34,6 +35,42 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [3.4.3] - 2026-10-21 +- chore: bump version to 3.4.3 by @github-actions[bot] +- chore(deps): bump dompurify from 3.4.13 to 3.4.16 (#4723) by @dependabot[bot] +- fix(release): delete the unstable release by name, leave drafts alone by @jekkos +- chore(deps): bump moment from 2.30.1 to 2.31.0 (#4722) by @dependabot[bot] +- chore(deps): bump brace-expansion (#4721) by @dependabot[bot] +- feat(items): optimize search, attribute filtering, and sort for items view (#4652) by @objecttothis +- fix(i18n): add missing toggle_cost_and_profit key to 32 locales (#4712) by @Rayan Abdul Cader +- fix(expenses): refresh table when date range or filters change (#4633) by @richardmilles +- fix(auth): return 401 JSON for AJAX requests on session timeout (#4728) by @jekkos +- docs: add test-method naming convention to AGENTS.md (#4735) by @jekkos +- fix(config): improve responsive layout, nested tabs behavior, and label theming (#4719) by @BudsieBuds +- fix(taxes): reject invalid characters in tax code, category, and jurisdiction names (#4733) by @jekkos +- fix(appconfig): refresh cached settings once and avoid stale same-request reads (#4732) by @jekkos +- Return early from sortAndFilterCart() when no items are printable (#4713) by @Rayan Abdul Cader +- fix: refresh cashups and items tables on filter and date range change (#4731) by @jekkos +- chore: update Node.js version to 22 and npm-check-updates to 23.1.0 in workflows and documentation (#4741) by @BudsieBuds +- fix(models): handle query() returning false in result-consuming methods (#4734) by @jekkos +- feat(reports): add detailed item sales report (#4538) by @enricodelarosa +- fix(taxes): reject negative tax rates and percentages (#4730) by @jekkos +- fix(sales): inline invoice/quote email CSS for DOMPDF PDFs (#4747) (#4749) by @jekkos +- docs: rewrite upgrade guide for the current CI4 install model (#4750) by @jekkos +- refactor: move Windows build scripts into tools/build, drop CI4 migration scripts (#4729) by @jekkos +- feat(config): scope config assets and add system info copy notifications (#4753) by @BudsieBuds +- docs: move BUILD.md, INSTALL.md, UPGRADE.md into docs/ (#4755) by @jekkos +- feat(tables): update loading text size and spinner to bootstrap-table (#4757) by @BudsieBuds +- feat(db): add MySQL 8.4 support (#4048) by @Alexander Laurent +- style(lang): add Georgian, normalize quote style across all locales, add parse tests (#4762) by @objecttothis +- feat(install): add one-line Ubuntu installer (#4550) by @jekkos +- fix(release): bump master version to 3.4.3-dev (#4752) by @jekkos +- fix(config): add invoice_enable and work_order_enable to default settings fallback (#4761) by @Sai Asish Y +- refactor(gulp): modernize build pipeline and streamline dependencies (#4759) by @BudsieBuds +- refactor(tests): normalize test namespaces to PSR-4 and clean up composer dependencies (#4758) by @BudsieBuds +- fix(ci): trigger PHP linting workflow on pull requests (#4764) by @BudsieBuds +- fix(security): pin patched PHP 8.2.35+ for CVE-2026-109618/109619 (#4767) by @jekkos + ## [3.4.2] - 2026-09-30 - Fix writable folder permission check (#4270) (#4273) by @jekkos - Extended payment delete fix (#4274) by @jekkos diff --git a/Dockerfile b/Dockerfile index 4180c9259..98cad2e15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.2-apache AS ospos +FROM php:8.2.35-apache AS ospos LABEL maintainer="jekkos" RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/README.md b/README.md index e0a47cd30..ee27758b4 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Any person or company found breaching the license agreement might find a bunch o ## 🙏 Credits -|
DigitalOcean
|
JetBrains
|
GitHub
| -| --- | --- | --- | -|
DigitalOcean Logo
|
IntelliJ IDEA Logo
|
GitHub Actions Logo
| -| Many thanks to [DigitalOcean](https://www.digitalocean.com) for providing the project with hosting credits. | Many thanks to [JetBrains](https://www.jetbrains.com/) for providing a free license of [IntelliJ IDEA](https://www.jetbrains.com/idea/) to kindly support the development of OSPOS. | Many thanks to [GitHub](https://github.com) for providing free continuous integration via GitHub Actions for open-source projects. | +|
JetBrains
|
GitHub
| +| --- | --- | +|
IntelliJ IDEA Logo
|
GitHub Actions Logo
| +| Many thanks to [JetBrains](https://www.jetbrains.com/) for providing a free license of [IntelliJ IDEA](https://www.jetbrains.com/idea/) to kindly support the development of OSPOS. | Many thanks to [GitHub](https://github.com) for providing free continuous integration via GitHub Actions for open-source projects. | diff --git a/app/Config/App.php b/app/Config/App.php index 3a5af0dd1..79c8d9f2e 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -12,7 +12,7 @@ class App extends BaseConfig * * @var string */ - public string $application_version = '3.4.3-dev'; + public string $application_version = '3.4.4-dev'; /** * This is the commit hash for the version you are currently using. diff --git a/app/Database/Migrations/20170501000000_initial_schema.php b/app/Database/Migrations/20170501000000_initial_schema.php index 057f398c4..8998071f2 100644 --- a/app/Database/Migrations/20170501000000_initial_schema.php +++ b/app/Database/Migrations/20170501000000_initial_schema.php @@ -25,7 +25,7 @@ class Migration_Initial_Schema extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/initial_schema.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/initial_schema.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20170501150000_upgrade_to_3_1_1.php b/app/Database/Migrations/20170501150000_upgrade_to_3_1_1.php index ce56b0d7b..bcb7bea51 100644 --- a/app/Database/Migrations/20170501150000_upgrade_to_3_1_1.php +++ b/app/Database/Migrations/20170501150000_upgrade_to_3_1_1.php @@ -42,7 +42,7 @@ class Migration_Upgrade_To_3_1_1 extends Migration } } - if (!executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql')) { + if (!executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql', false, $this->db)) { throw new DatabaseException('Migration script 3.0.2_to_3.1.1.sql failed. Check logs for details.'); } diff --git a/app/Database/Migrations/20180225100000_upgrade_to_3_2_0.php b/app/Database/Migrations/20180225100000_upgrade_to_3_2_0.php index 83adfbcd0..e122a4346 100644 --- a/app/Database/Migrations/20180225100000_upgrade_to_3_2_0.php +++ b/app/Database/Migrations/20180225100000_upgrade_to_3_2_0.php @@ -12,7 +12,7 @@ class Migration_Upgrade_To_3_2_0 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.1.1_to_3.2.0.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.1.1_to_3.2.0.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20180501100000_upgrade_to_3_2_1.php b/app/Database/Migrations/20180501100000_upgrade_to_3_2_1.php index 6446e5524..5a2f3c7bb 100644 --- a/app/Database/Migrations/20180501100000_upgrade_to_3_2_1.php +++ b/app/Database/Migrations/20180501100000_upgrade_to_3_2_1.php @@ -12,7 +12,7 @@ class Migration_Upgrade_To_3_2_1 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.2.0_to_3.2.1.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.2.0_to_3.2.1.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20181015100000_attributes.php b/app/Database/Migrations/20181015100000_attributes.php index 076015b55..181e01add 100644 --- a/app/Database/Migrations/20181015100000_attributes.php +++ b/app/Database/Migrations/20181015100000_attributes.php @@ -12,7 +12,7 @@ class Migration_Attributes extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_attributes.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_attributes.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190111270000_upgrade_to_3_3_0.php b/app/Database/Migrations/20190111270000_upgrade_to_3_3_0.php index 9604aeb5e..452ac3109 100644 --- a/app/Database/Migrations/20190111270000_upgrade_to_3_3_0.php +++ b/app/Database/Migrations/20190111270000_upgrade_to_3_3_0.php @@ -12,7 +12,7 @@ class Migration_Upgrade_To_3_3_0 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.2.1_to_3.3.0.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.2.1_to_3.3.0.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190129212600_indiagst.php b/app/Database/Migrations/20190129212600_indiagst.php index 2c5ff76e5..a19f70edd 100644 --- a/app/Database/Migrations/20190129212600_indiagst.php +++ b/app/Database/Migrations/20190129212600_indiagst.php @@ -17,7 +17,7 @@ class Migration_IndiaGST extends Migration // If number of entries is greater than zero then the tax data needs to be migrated helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst.sql', false, $this->db); $count_of_tax_codes = $this->get_count_of_tax_code_entries(); diff --git a/app/Database/Migrations/20190213210000_indiagst1.php b/app/Database/Migrations/20190213210000_indiagst1.php index d45d7a3ca..006b8b2d4 100644 --- a/app/Database/Migrations/20190213210000_indiagst1.php +++ b/app/Database/Migrations/20190213210000_indiagst1.php @@ -12,7 +12,7 @@ class Migration_IndiaGST1 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst1.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst1.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190220210000_indiagst2.php b/app/Database/Migrations/20190220210000_indiagst2.php index 12cf5364a..d6e499806 100644 --- a/app/Database/Migrations/20190220210000_indiagst2.php +++ b/app/Database/Migrations/20190220210000_indiagst2.php @@ -12,7 +12,7 @@ class Migration_IndiaGST2 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst2.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_indiagst2.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190301124900_decimal_attribute_type.php b/app/Database/Migrations/20190301124900_decimal_attribute_type.php index a8235ac52..f20602994 100644 --- a/app/Database/Migrations/20190301124900_decimal_attribute_type.php +++ b/app/Database/Migrations/20190301124900_decimal_attribute_type.php @@ -12,7 +12,7 @@ class Migration_decimal_attribute_type extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_decimal_attribute_type.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_decimal_attribute_type.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190317102600_add_iso_4217.php b/app/Database/Migrations/20190317102600_add_iso_4217.php index 5778fe2b3..b683ed5f2 100644 --- a/app/Database/Migrations/20190317102600_add_iso_4217.php +++ b/app/Database/Migrations/20190317102600_add_iso_4217.php @@ -12,7 +12,7 @@ class Migration_add_iso_4217 extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_add_iso_4217.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_add_iso_4217.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190427100000_paymenttracking.php b/app/Database/Migrations/20190427100000_paymenttracking.php index 11c1cfe6b..72ce36a88 100644 --- a/app/Database/Migrations/20190427100000_paymenttracking.php +++ b/app/Database/Migrations/20190427100000_paymenttracking.php @@ -12,7 +12,7 @@ class Migration_PaymentTracking extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_paymenttracking.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_paymenttracking.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190502100000_refundtracking.php b/app/Database/Migrations/20190502100000_refundtracking.php index 18c157462..e6e376c33 100644 --- a/app/Database/Migrations/20190502100000_refundtracking.php +++ b/app/Database/Migrations/20190502100000_refundtracking.php @@ -12,7 +12,7 @@ class Migration_RefundTracking extends Migration public function up(): void { helper(['migration', 'locale']); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_refundtracking.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_refundtracking.sql', false, $this->db); // Add missing cash_refund amounts to payments table $decimals = totals_decimals(); diff --git a/app/Database/Migrations/20190612100000_dbfix.php b/app/Database/Migrations/20190612100000_dbfix.php index 65377575f..2fdce4f33 100644 --- a/app/Database/Migrations/20190612100000_dbfix.php +++ b/app/Database/Migrations/20190612100000_dbfix.php @@ -12,7 +12,7 @@ class Migration_DBFix extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_dbfix.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_dbfix.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20190615100000_fix_attribute_datetime.php b/app/Database/Migrations/20190615100000_fix_attribute_datetime.php index f07a3af89..f4762815e 100644 --- a/app/Database/Migrations/20190615100000_fix_attribute_datetime.php +++ b/app/Database/Migrations/20190615100000_fix_attribute_datetime.php @@ -12,7 +12,7 @@ class Migration_fix_attribute_datetime extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_fix_attribute_datetime.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.0_fix_attribute_datetime.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20191231100000_paymentdatefix.php b/app/Database/Migrations/20191231100000_paymentdatefix.php index 8f2a30aeb..4efecd771 100644 --- a/app/Database/Migrations/20191231100000_paymentdatefix.php +++ b/app/Database/Migrations/20191231100000_paymentdatefix.php @@ -12,7 +12,7 @@ class Migration_PaymentDateFix extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_paymentdatefix.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_paymentdatefix.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20200125100000_saleschangeprice.php b/app/Database/Migrations/20200125100000_saleschangeprice.php index 81b6051b8..a01d86d04 100644 --- a/app/Database/Migrations/20200125100000_saleschangeprice.php +++ b/app/Database/Migrations/20200125100000_saleschangeprice.php @@ -12,7 +12,7 @@ class Migration_SalesChangePrice extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_saleschangeprice.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_saleschangeprice.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20200819000000_modify_attr_links_constraint.php b/app/Database/Migrations/20200819000000_modify_attr_links_constraint.php index b23172236..871011e93 100644 --- a/app/Database/Migrations/20200819000000_modify_attr_links_constraint.php +++ b/app/Database/Migrations/20200819000000_modify_attr_links_constraint.php @@ -12,7 +12,7 @@ class Migration_modify_attr_links_constraint extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_modify_attr_links_constraint.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.2_modify_attr_links_constraint.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20201110000000_add_item_kit_number.php b/app/Database/Migrations/20201110000000_add_item_kit_number.php index 2f307a66f..f4d6a9ccc 100644 --- a/app/Database/Migrations/20201110000000_add_item_kit_number.php +++ b/app/Database/Migrations/20201110000000_add_item_kit_number.php @@ -12,7 +12,7 @@ class Migration_add_item_kit_number extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.3_add_kits_item_number.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.3_add_kits_item_number.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20210103000000_modify_session_datatype.php b/app/Database/Migrations/20210103000000_modify_session_datatype.php index 5c24aab75..7334580d4 100644 --- a/app/Database/Migrations/20210103000000_modify_session_datatype.php +++ b/app/Database/Migrations/20210103000000_modify_session_datatype.php @@ -12,7 +12,7 @@ class Migration_modify_session_datatype extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.4_modify_session_datatype.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.3.4_modify_session_datatype.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20210422000000_database_optimizations.php b/app/Database/Migrations/20210422000000_database_optimizations.php index 74aeeedef..801a628ac 100644 --- a/app/Database/Migrations/20210422000000_database_optimizations.php +++ b/app/Database/Migrations/20210422000000_database_optimizations.php @@ -113,7 +113,7 @@ class Migration_database_optimizations extends Migration } $this->db->transComplete(); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_database_optimizations.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_database_optimizations.sql', false, $this->db); log_message('info', 'Finished migrating database optimizations.'); } diff --git a/app/Database/Migrations/20220127000000_convertToCI4.php b/app/Database/Migrations/20220127000000_convertToCI4.php index f860c7aaa..bc8881600 100644 --- a/app/Database/Migrations/20220127000000_convertToCI4.php +++ b/app/Database/Migrations/20220127000000_convertToCI4.php @@ -27,7 +27,7 @@ class ConvertToCI4 extends Migration { helper('migration'); - if (!executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_CI4Conversion.sql')) { + if (!executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_CI4Conversion.sql', false, $this->db)) { throw new DatabaseException('Migration script 3.4.0_CI4Conversion.sql failed. Check logs for details.'); } diff --git a/app/Database/Migrations/20240826000000_fix_duplicate_attributes.php b/app/Database/Migrations/20240826000000_fix_duplicate_attributes.php index 6822a1589..66fd030e4 100644 --- a/app/Database/Migrations/20240826000000_fix_duplicate_attributes.php +++ b/app/Database/Migrations/20240826000000_fix_duplicate_attributes.php @@ -28,7 +28,7 @@ class fix_duplicate_attributes extends Migration dropForeignKeyConstraints($foreignKeys, 'attribute_links'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_attribute_links_unique_constraint.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_attribute_links_unique_constraint.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20250425000000_sessions_migration.php b/app/Database/Migrations/20250425000000_sessions_migration.php index 7402dda81..dfdf514b4 100644 --- a/app/Database/Migrations/20250425000000_sessions_migration.php +++ b/app/Database/Migrations/20250425000000_sessions_migration.php @@ -12,7 +12,7 @@ class Migration_sessions_migration extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.1_migrate_sessions_table.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.1_migrate_sessions_table.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20250522000000_AttributeLinksUniqueConstraint.php b/app/Database/Migrations/20250522000000_AttributeLinksUniqueConstraint.php index 884d95b68..556aceb13 100644 --- a/app/Database/Migrations/20250522000000_AttributeLinksUniqueConstraint.php +++ b/app/Database/Migrations/20250522000000_AttributeLinksUniqueConstraint.php @@ -21,7 +21,7 @@ class AttributeLinksUniqueConstraint extends Migration dropForeignKeyConstraints($foreignKeys, 'attribute_links'); dropColumnIfExists('ospos_attribute_links', 'generated_unique_column'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.1_attribute_links_unique_constraint.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.1_attribute_links_unique_constraint.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20250716170000_MissingConfigKeys.php b/app/Database/Migrations/20250716170000_MissingConfigKeys.php index 85617e11e..5c2df663f 100644 --- a/app/Database/Migrations/20250716170000_MissingConfigKeys.php +++ b/app/Database/Migrations/20250716170000_MissingConfigKeys.php @@ -12,7 +12,7 @@ class Migration_MissingConfigKeys extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.2_missing_config_keys.sql', true); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.2_missing_config_keys.sql', true, $this->db); } /** diff --git a/app/Database/Migrations/20250729170000_NullableTaxCategoryId.php b/app/Database/Migrations/20250729170000_NullableTaxCategoryId.php index 27367797d..d2ae8376a 100644 --- a/app/Database/Migrations/20250729170000_NullableTaxCategoryId.php +++ b/app/Database/Migrations/20250729170000_NullableTaxCategoryId.php @@ -12,7 +12,7 @@ class Migration_NullableTaxCategoryId extends Migration public function up(): void { helper('migration'); - executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.2_nullable_tax_category_id.sql'); + executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.2_nullable_tax_category_id.sql', false, $this->db); } /** diff --git a/app/Database/Migrations/20260405000000_Mysql84Utf8mb4Conversion.php b/app/Database/Migrations/20260405000000_Mysql84Utf8mb4Conversion.php index cd1ba2383..f6a34d6c6 100644 --- a/app/Database/Migrations/20260405000000_Mysql84Utf8mb4Conversion.php +++ b/app/Database/Migrations/20260405000000_Mysql84Utf8mb4Conversion.php @@ -21,7 +21,7 @@ class Migration_Mysql84Utf8mb4Conversion extends Migration } $script = APPPATH . 'Database/Migrations/sqlscripts/3.4.3_mysql84_utf8mb4_conversion.sql'; - if (!executeScript($script)) { + if (!executeScript($script, false, $this->db)) { throw new RuntimeException('Failed to execute utf8mb4 conversion migration: ' . $script); } diff --git a/app/Database/Seeds/TestDatabaseBootstrapSeeder.php b/app/Database/Seeds/TestDatabaseBootstrapSeeder.php index 134d10406..0de39a1b0 100644 --- a/app/Database/Seeds/TestDatabaseBootstrapSeeder.php +++ b/app/Database/Seeds/TestDatabaseBootstrapSeeder.php @@ -36,7 +36,7 @@ class TestDatabaseBootstrapSeeder extends Seeder $serverConn->close(); // The application's shared 'tests' connection caches listTables() - // results in dataCache['table_names'']. Because we dropped and + // results in dataCache['table_names']. Because we dropped and // recreated the schema on a SEPARATE server connection above, that // cache is now stale. Reset it so subsequent listTables()/tableExists() // calls re-query the server instead of trusting a dropped schema's table diff --git a/app/Helpers/migration_helper.php b/app/Helpers/migration_helper.php index c8dd3d71b..0d030b9bb 100644 --- a/app/Helpers/migration_helper.php +++ b/app/Helpers/migration_helper.php @@ -8,9 +8,10 @@ use Config\Database; * Executes a SQL migration script. * @param string $path Path to migration script. * @param bool $withTransaction Whether to wrap execution in a transaction. + * @param BaseConnection|null $db Connection to use (defaults to the default connection). * @return bool Whether the migration executed successfully. */ -function executeScript(string $path, bool $withTransaction = false): bool +function executeScript(string $path, bool $withTransaction = false, ?BaseConnection $db = null): bool { $version = explode('_', basename($path, '.sql'), 2)[0]; log_message('info', "Migrating to $version (file: $path)"); @@ -18,7 +19,7 @@ function executeScript(string $path, bool $withTransaction = false): bool $sqls = explode(';', file_get_contents($path)); array_pop($sqls); - $db = Database::connect(); + $db ??= Database::connect(); if ($withTransaction) { $db->transStart(); @@ -84,8 +85,8 @@ function dropForeignKeyConstraints(array $foreignKeys, string $table): void * TODO: This function should be moved to a more global location since it may be needed outside of migrations. * @return string The prefix before overriding. */ -function overridePrefix(string $prefix = ''): string { - $db = Database::connect(); +function overridePrefix(string $prefix = '', ?BaseConnection $db = null): string { + $db ??= Database::connect(); $originalPrefix = $db->getPrefix(); $db->setPrefix($prefix); @@ -136,47 +137,50 @@ function createPrimaryKey(string $table, string $index): void { function dropAllForeignKeyConstraints(?string $table = null, ?string $column = null, ?BaseConnection $db = null): array { $db ??= Database::connect(); $prefixedTable = $table !== null ? $db->getPrefix() . $table : null; - $prefix = overridePrefix(); - - $builder = $db->table('information_schema.KEY_COLUMN_USAGE kcu'); - $builder->distinct(); - $builder->select('kcu.CONSTRAINT_NAME, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME, kcu.ORDINAL_POSITION, rc.DELETE_RULE, rc.UPDATE_RULE'); - $builder->join( - 'information_schema.REFERENTIAL_CONSTRAINTS rc', - 'kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA AND kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME AND kcu.TABLE_NAME = rc.TABLE_NAME', - 'left' - ); - - if ($table !== null && $column !== null) { - $scopedBuilder = $db->table('information_schema.KEY_COLUMN_USAGE scoped'); - $scopedBuilder->distinct(); - $scopedBuilder->select('scoped.CONSTRAINT_NAME, scoped.TABLE_NAME'); - $scopedBuilder->where('scoped.TABLE_SCHEMA', $db->database); - $scopedBuilder->groupStart(); - $scopedBuilder->where('scoped.REFERENCED_TABLE_NAME', $prefixedTable); - $scopedBuilder->where('scoped.REFERENCED_COLUMN_NAME', $column); - $scopedBuilder->groupEnd(); - $scopedBuilder->orGroupStart(); - $scopedBuilder->where('scoped.TABLE_NAME', $prefixedTable); - $scopedBuilder->where('scoped.COLUMN_NAME', $column); - $scopedBuilder->groupEnd(); + $prefix = overridePrefix('', $db); + try { + $builder = $db->table('information_schema.KEY_COLUMN_USAGE kcu'); + $builder->distinct(); + $builder->select('kcu.CONSTRAINT_NAME, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME, kcu.ORDINAL_POSITION, rc.DELETE_RULE, rc.UPDATE_RULE'); $builder->join( - '(' . $scopedBuilder->getCompiledSelect() . ') matched', - 'matched.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND matched.TABLE_NAME = kcu.TABLE_NAME', - 'inner', - false + 'information_schema.REFERENTIAL_CONSTRAINTS rc', + 'kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA AND kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME AND kcu.TABLE_NAME = rc.TABLE_NAME', + 'left' ); + + if ($table !== null && $column !== null) { + $scopedBuilder = $db->table('information_schema.KEY_COLUMN_USAGE scoped'); + $scopedBuilder->distinct(); + $scopedBuilder->select('scoped.CONSTRAINT_NAME, scoped.TABLE_NAME'); + $scopedBuilder->where('scoped.TABLE_SCHEMA', $db->database); + $scopedBuilder->groupStart(); + $scopedBuilder->where('scoped.REFERENCED_TABLE_NAME', $prefixedTable); + $scopedBuilder->where('scoped.REFERENCED_COLUMN_NAME', $column); + $scopedBuilder->groupEnd(); + $scopedBuilder->orGroupStart(); + $scopedBuilder->where('scoped.TABLE_NAME', $prefixedTable); + $scopedBuilder->where('scoped.COLUMN_NAME', $column); + $scopedBuilder->groupEnd(); + + $builder->join( + '(' . $scopedBuilder->getCompiledSelect() . ') matched', + 'matched.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND matched.TABLE_NAME = kcu.TABLE_NAME', + 'inner', + false + ); + } + + $builder->where('kcu.TABLE_SCHEMA', $db->database); + $builder->where('rc.CONSTRAINT_NAME IS NOT NULL', null, false); + $builder->orderBy('kcu.CONSTRAINT_NAME'); + $builder->orderBy('kcu.ORDINAL_POSITION'); + + $result = $builder->get(); + } finally { + overridePrefix($prefix, $db); } - $builder->where('kcu.TABLE_SCHEMA', $db->database); - $builder->where('rc.CONSTRAINT_NAME IS NOT NULL', null, false); - $builder->orderBy('kcu.CONSTRAINT_NAME'); - $builder->orderBy('kcu.ORDINAL_POSITION'); - - $result = $builder->get(); - overridePrefix($prefix); - if ($result === false) { $detail = 'unknown error'; try { @@ -346,18 +350,20 @@ function foreignKeyExists(string $constraintName, string $tableName): bool { $prefix = overridePrefix(); - $db = Database::connect(); - $builder = $db->table('INFORMATION_SCHEMA.TABLE_CONSTRAINTS'); - $builder->select('CONSTRAINT_NAME'); - $builder->where('TABLE_SCHEMA', $db->database); - $builder->where('TABLE_NAME', $prefix . $tableName); - $builder->where('CONSTRAINT_TYPE', 'FOREIGN KEY'); - $builder->where('CONSTRAINT_NAME', $constraintName); - $query = $builder->get(); + try { + $db = Database::connect(); + $builder = $db->table('INFORMATION_SCHEMA.TABLE_CONSTRAINTS'); + $builder->select('CONSTRAINT_NAME'); + $builder->where('TABLE_SCHEMA', $db->database); + $builder->where('TABLE_NAME', $prefix . $tableName); + $builder->where('CONSTRAINT_TYPE', 'FOREIGN KEY'); + $builder->where('CONSTRAINT_NAME', $constraintName); + $query = $builder->get(); - overridePrefix($prefix); - - return $query->getNumRows() > 0; + return $query->getNumRows() > 0; + } finally { + overridePrefix($prefix); + } } /** @@ -371,23 +377,26 @@ function dropColumnIfExists(string $table, string $column): void { $prefix = overridePrefix(); - $db = Database::connect(); - $builder = $db->table('information_schema.COLUMNS'); + try { + $db = Database::connect(); + $builder = $db->table('information_schema.COLUMNS'); - // Check if the column exists in the table - $builder->select('COLUMN_NAME') - ->where('TABLE_SCHEMA', $db->database) - ->where('TABLE_NAME', $prefix . $table) - ->where('COLUMN_NAME', $column); + // Check if the column exists in the table + $builder->select('COLUMN_NAME') + ->where('TABLE_SCHEMA', $db->database) + ->where('TABLE_NAME', $prefix . $table) + ->where('COLUMN_NAME', $column); - $query = $builder->get(); + $query = $builder->get(); - if ($query->getNumRows() > 0) - { - // Drop the column if it exists - $db->query("ALTER TABLE `" . $prefix . "$table` DROP COLUMN `$column`"); + if ($query->getNumRows() > 0) + { + // Drop the column if it exists + $db->query("ALTER TABLE `" . $prefix . "$table` DROP COLUMN `$column`"); + } + } finally { + overridePrefix($prefix); } - overridePrefix($prefix); } /** diff --git a/composer.json b/composer.json index 7db196cbd..885d0d55a 100644 --- a/composer.json +++ b/composer.json @@ -25,27 +25,22 @@ "matrix": "https://matrix.to/#/#opensourcepos_Lobby:gitter.im" }, "require": { - "php": "^8.2", + "php": ">=8.2.35 <8.3 || >=8.3.36 <8.4 || >=8.4.27", "ext-intl": "*", "codeigniter4/framework": "4.7.4", "dompdf/dompdf": "^3.1.6", - "ezyang/htmlpurifier": "^4.17", - "laminas/laminas-escaper": "2.18.0", + "ezyang/htmlpurifier": "^4.19", "openspout/openspout": "^4.28", - "paragonie/random_compat": "^2.0.21", "picqer/php-barcode-generator": "^2.4.0", "setasign/tfpdf": "^1.34", "tamtamchik/namecase": "^3.0.0" }, "require-dev": { - "codeigniter/coding-standard": "^1.8", - "fakerphp/faker": "^1.23.0", - "friendsofphp/php-cs-fixer": "^3.47.1", + "codeigniter/coding-standard": "^1.9", + "friendsofphp/php-cs-fixer": "^3.95", "kint-php/kint": "^5.0.4", - "mikey179/vfsstream": "^1.6", - "nexusphp/cs-config": "^3.6", - "phpunit/phpunit": "^10.5.16 || ^11.2", - "predis/predis": "^1.1 || ^2.0" + "nexusphp/cs-config": "^3.29", + "phpunit/phpunit": "^10.5.16 || ^11.2" }, "replace": { "psr/log": "*" @@ -53,6 +48,7 @@ "autoload": { "psr-4": { "App\\": "app/", + "Config\\": "app/Config/", "CodeIgniter\\": "vendor/codeigniter4/framework/system/" }, "exclude-from-classmap": [ diff --git a/composer.lock b/composer.lock index 47014be3d..9d828d02d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4f1ca5347f1630e56c90449f4e3d1ce2", + "content-hash": "6c022bc8409249b371ab549c76a41f74", "packages": [ { "name": "codeigniter4/framework", @@ -240,16 +240,16 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.19.0", + "version": "v4.19.1", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + "reference": "5e0539132d934f936fbae2c88b55d58f9438623f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", - "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/5e0539132d934f936fbae2c88b55d58f9438623f", + "reference": "5e0539132d934f936fbae2c88b55d58f9438623f", "shasum": "" }, "require": { @@ -295,9 +295,9 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.1" }, - "time": "2025-10-17T16:34:55+00:00" + "time": "2026-09-15T15:42:51+00:00" }, { "name": "laminas/laminas-escaper", @@ -362,24 +362,24 @@ }, { "name": "masterminds/html5", - "version": "2.10.1", + "version": "2.11.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "fd5018f6815fff903946d0564977b44ce8010e29" + "reference": "a1e7a2f88ee13635d86fc61cfbdf2306a76ddfc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fd5018f6815fff903946d0564977b44ce8010e29", - "reference": "fd5018f6815fff903946d0564977b44ce8010e29", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/a1e7a2f88ee13635d86fc61cfbdf2306a76ddfc7", + "reference": "a1e7a2f88ee13635d86fc61cfbdf2306a76ddfc7", "shasum": "" }, "require": { "ext-dom": "*", - "php": ">=5.3.0" + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9 || ^10" + "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10" }, "type": "library", "extra": { @@ -423,9 +423,9 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.10.1" + "source": "https://github.com/Masterminds/html5-php/tree/2.11.0" }, - "time": "2026-06-23T18:43:15+00:00" + "time": "2026-08-18T06:18:41+00:00" }, { "name": "openspout/openspout", @@ -520,60 +520,6 @@ ], "time": "2025-09-03T16:03:54+00:00" }, - { - "name": "paragonie/random_compat", - "version": "v2.0.21", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" - }, - "time": "2022-02-16T17:07:03+00:00" - }, { "name": "picqer/php-barcode-generator", "version": "v2.4.2", @@ -663,35 +609,33 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v9.4.0", + "version": "v9.5.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f" + "reference": "f284e63b6e891e0c28631e54ba06c3ed102a9ef3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f", - "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/f284e63b6e891e0c28631e54ba06c3ed102a9ef3", + "reference": "f284e63b6e891e0c28631e54ba06c3ed102a9ef3", "shasum": "" }, "require": { "ext-iconv": "*", - "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4" + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "1.4.0", "phpstan/extension-installer": "1.4.3", - "phpstan/phpstan": "1.12.33 || 2.2.2", - "phpstan/phpstan-phpunit": "1.4.2 || 2.0.16", - "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.11", - "phpunit/phpunit": "8.5.52", + "phpstan/phpstan": "1.12.33 || 2.2.9", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.18", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.12", + "phpunit/phpunit": "8.5.54", "rawr/phpunit-data-provider": "3.3.1", - "rector/rector": "1.2.10 || 2.4.6", - "rector/type-perfect": "1.0.0 || 2.1.3", - "squizlabs/php_codesniffer": "4.0.1", - "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.3" + "rector/rector": "1.2.10 || 2.6.2", + "rector/type-perfect": "1.0.0 || 2.1.4", + "squizlabs/php_codesniffer": "4.0.4" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -699,7 +643,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.5.x-dev" + "dev-main": "9.6.x-dev" } }, "autoload": { @@ -737,9 +681,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.4.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.5.0" }, - "time": "2026-06-18T15:10:53+00:00" + "time": "2026-09-20T15:02:00+00:00" }, { "name": "setasign/tfpdf", @@ -797,16 +741,16 @@ }, { "name": "tamtamchik/namecase", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/tamtamchik/namecase.git", - "reference": "f963f321a3afbde83f1bb1bda1d53b848c7015dd" + "reference": "5963ef26311f87dac0e9b3c6a0e7f3976068176b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tamtamchik/namecase/zipball/f963f321a3afbde83f1bb1bda1d53b848c7015dd", - "reference": "f963f321a3afbde83f1bb1bda1d53b848c7015dd", + "url": "https://api.github.com/repos/tamtamchik/namecase/zipball/5963ef26311f87dac0e9b3c6a0e7f3976068176b", + "reference": "5963ef26311f87dac0e9b3c6a0e7f3976068176b", "shasum": "" }, "require": { @@ -822,7 +766,7 @@ "branch-alias": { "1.x": "1.x-dev", "2.x": "2.x-dev", - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -855,152 +799,9 @@ ], "support": { "issues": "https://github.com/tamtamchik/namecase/issues", - "source": "https://github.com/tamtamchik/namecase/tree/3.0.0" + "source": "https://github.com/tamtamchik/namecase/tree/3.1.0" }, - "time": "2023-01-26T15:07:18+00:00" - }, - { - "name": "thecodingmachine/safe", - "version": "v3.4.0", - "source": { - "type": "git", - "url": "https://github.com/thecodingmachine/safe.git", - "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", - "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.4", - "phpstan/phpstan": "^2", - "phpunit/phpunit": "^10", - "squizlabs/php_codesniffer": "^3.2" - }, - "type": "library", - "autoload": { - "files": [ - "lib/special_cases.php", - "generated/apache.php", - "generated/apcu.php", - "generated/array.php", - "generated/bzip2.php", - "generated/calendar.php", - "generated/classobj.php", - "generated/com.php", - "generated/cubrid.php", - "generated/curl.php", - "generated/datetime.php", - "generated/dir.php", - "generated/eio.php", - "generated/errorfunc.php", - "generated/exec.php", - "generated/fileinfo.php", - "generated/filesystem.php", - "generated/filter.php", - "generated/fpm.php", - "generated/ftp.php", - "generated/funchand.php", - "generated/gettext.php", - "generated/gmp.php", - "generated/gnupg.php", - "generated/hash.php", - "generated/ibase.php", - "generated/ibmDb2.php", - "generated/iconv.php", - "generated/image.php", - "generated/imap.php", - "generated/info.php", - "generated/inotify.php", - "generated/json.php", - "generated/ldap.php", - "generated/libxml.php", - "generated/lzf.php", - "generated/mailparse.php", - "generated/mbstring.php", - "generated/misc.php", - "generated/mysql.php", - "generated/mysqli.php", - "generated/network.php", - "generated/oci8.php", - "generated/opcache.php", - "generated/openssl.php", - "generated/outcontrol.php", - "generated/pcntl.php", - "generated/pcre.php", - "generated/pgsql.php", - "generated/posix.php", - "generated/ps.php", - "generated/pspell.php", - "generated/readline.php", - "generated/rnp.php", - "generated/rpminfo.php", - "generated/rrd.php", - "generated/sem.php", - "generated/session.php", - "generated/shmop.php", - "generated/sockets.php", - "generated/sodium.php", - "generated/solr.php", - "generated/spl.php", - "generated/sqlsrv.php", - "generated/ssdeep.php", - "generated/ssh2.php", - "generated/stream.php", - "generated/strings.php", - "generated/swoole.php", - "generated/uodbc.php", - "generated/uopz.php", - "generated/url.php", - "generated/var.php", - "generated/xdiff.php", - "generated/xml.php", - "generated/xmlrpc.php", - "generated/yaml.php", - "generated/yaz.php", - "generated/zip.php", - "generated/zlib.php" - ], - "classmap": [ - "lib/DateTime.php", - "lib/DateTimeImmutable.php", - "lib/Exceptions/", - "generated/Exceptions/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHP core functions that throw exceptions instead of returning FALSE on error", - "support": { - "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" - }, - "funding": [ - { - "url": "https://github.com/OskarStark", - "type": "github" - }, - { - "url": "https://github.com/shish", - "type": "github" - }, - { - "url": "https://github.com/silasjoisten", - "type": "github" - }, - { - "url": "https://github.com/staabm", - "type": "github" - } - ], - "time": "2026-02-04T18:08:13+00:00" + "time": "2026-07-05T23:53:27+00:00" } ], "packages-dev": [ @@ -1070,21 +871,21 @@ }, { "name": "codeigniter/coding-standard", - "version": "v1.9.1", + "version": "v1.9.2", "source": { "type": "git", "url": "https://github.com/CodeIgniter/coding-standard.git", - "reference": "c7d227d3d3d0f2270405c8317c5e8c55f2262956" + "reference": "e9c690ebb86140a2ddf874940b0758c4b88110ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CodeIgniter/coding-standard/zipball/c7d227d3d3d0f2270405c8317c5e8c55f2262956", - "reference": "c7d227d3d3d0f2270405c8317c5e8c55f2262956", + "url": "https://api.github.com/repos/CodeIgniter/coding-standard/zipball/e9c690ebb86140a2ddf874940b0758c4b88110ce", + "reference": "e9c690ebb86140a2ddf874940b0758c4b88110ce", "shasum": "" }, "require": { "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.94", + "friendsofphp/php-cs-fixer": "^3.95", "nexusphp/cs-config": "^3.28", "php": "^8.2" }, @@ -1120,32 +921,33 @@ "slack": "https://codeigniterchat.slack.com", "source": "https://github.com/CodeIgniter/coding-standard" }, - "time": "2026-02-17T18:41:24+00:00" + "time": "2026-04-11T20:06:02+00:00" }, { "name": "composer/pcre", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.10" + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { @@ -1183,7 +985,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { @@ -1193,26 +995,22 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-11-12T16:29:46+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { "name": "composer/semver", - "version": "3.4.4", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + "reference": "528948210cc46f967a4b98e83b19e76f2b504993" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "url": "https://api.github.com/repos/composer/semver/zipball/528948210cc46f967a4b98e83b19e76f2b504993", + "reference": "528948210cc46f967a4b98e83b19e76f2b504993", "shasum": "" }, "require": { @@ -1254,7 +1052,7 @@ "homepage": "http://robbast.nl" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "Version comparison library that offers utilities, version constraint parsing and validation.", "keywords": [ "semantic", "semver", @@ -1264,7 +1062,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" + "source": "https://github.com/composer/semver/tree/3.5.1" }, "funding": [ { @@ -1276,7 +1074,7 @@ "type": "github" } ], - "time": "2025-08-20T19:15:30+00:00" + "time": "2026-10-05T13:12:12+00:00" }, { "name": "composer/xdebug-handler", @@ -1344,6 +1142,75 @@ ], "time": "2024-05-06T16:37:16+00:00" }, + { + "name": "ergebnis/agent-detector", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/agent-detector.git", + "reference": "0cd815cec1207873c37624d33f0f2853523152f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/0cd815cec1207873c37624d33f0f2853523152f3", + "reference": "0cd815cec1207873c37624d33f0f2853523152f3", + "shasum": "" + }, + "require": { + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.54.0", + "ergebnis/license": "^2.8.0", + "ergebnis/php-cs-fixer-config": "^6.60.2", + "ergebnis/phpstan-rules": "^2.14.0", + "ergebnis/phpunit-slow-test-detector": "^2.26.0", + "ergebnis/rector-rules": "^1.18.5", + "fakerphp/faker": "^1.24.1", + "infection/infection": "^0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.2.14", + "phpstan/phpstan-deprecation-rules": "^2.0.5", + "phpstan/phpstan-phpunit": "^2.1.1", + "phpstan/phpstan-strict-rules": "^2.1.0", + "phpunit/phpunit": "^9.6.34", + "rector/rector": "^2.7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.3-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\AgentDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides a detector for detecting the presence of an agent.", + "homepage": "https://github.com/ergebnis/agent-detector", + "support": { + "issues": "https://github.com/ergebnis/agent-detector/issues", + "security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/agent-detector" + }, + "time": "2026-10-15T11:13:32+00:00" + }, { "name": "evenement/evenement", "version": "v3.0.2", @@ -1391,88 +1258,25 @@ }, "time": "2023-08-08T05:53:35+00:00" }, - { - "name": "fakerphp/faker", - "version": "v1.24.1", - "source": { - "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "conflict": { - "fzaninotto/faker": "*" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "doctrine/persistence": "^1.3 || ^2.0", - "ext-intl": "*", - "phpunit/phpunit": "^9.5.26", - "symfony/phpunit-bridge": "^5.4.16" - }, - "suggest": { - "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." - }, - "type": "library", - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" - }, - "time": "2024-11-21T13:46:39+00:00" - }, { "name": "fidry/cpu-core-counter", - "version": "1.3.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + "reference": "d384a7d0d6e67aa497327260ca95023e92dc2032" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/d384a7d0d6e67aa497327260ca95023e92dc2032", + "reference": "d384a7d0d6e67aa497327260ca95023e92dc2032", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "fidry/makefile": "^0.2.0", + "fidry/makefile": "^0.2.0 || ^1.1.2", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", "phpstan/phpstan": "^2.0", @@ -1480,6 +1284,7 @@ "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "symfony/polyfill-mbstring": "^1.33", "webmozarts/strict-phpunit": "^7.5" }, "type": "library", @@ -1505,7 +1310,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.4.1" }, "funding": [ { @@ -1513,26 +1318,27 @@ "type": "github" } ], - "time": "2025-08-14T07:29:31+00:00" + "time": "2026-09-29T22:11:39+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.94.2", + "version": "v3.95.27", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63" + "reference": "1d0c68ee5f37446d8ad5970b82384cd1bb0a3317" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7787ceff91365ba7d623ec410b8f429cdebb4f63", - "reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1d0c68ee5f37446d8ad5970b82384cd1bb0a3317", + "reference": "1d0c68ee5f37446d8ad5970b82384cd1bb0a3317", "shasum": "" }, "require": { "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", + "ergebnis/agent-detector": "^1.2", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", @@ -1543,32 +1349,31 @@ "react/event-loop": "^1.5", "react/socket": "^1.16", "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0 || ^9.0", "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.33", - "symfony/polyfill-php80": "^1.33", - "symfony/polyfill-php81": "^1.33", - "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-mbstring": "^1.37", + "symfony/polyfill-php80": "^1.37", + "symfony/polyfill-php81": "^1.37", + "symfony/polyfill-php84": "^1.37", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.7.1", - "infection/infection": "^0.32.3", - "justinrainbow/json-schema": "^6.6.4", + "facile-it/paraunit": "^1.3.1 || ^2.11.0", + "infection/infection": "^0.32.7", + "justinrainbow/json-schema": "^6.12.0", "keradus/cli-executor": "^2.3", - "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.9.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7", - "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51", - "symfony/polyfill-php85": "^1.33", - "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4", - "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8", + "phpunit/phpunit": "^9.6.36 || ^10.5.64 || ^11.5.56 || ^12.5.35 || ^13.3.3", + "symfony/polyfill-php85": "^1.41", + "symfony/var-dumper": "^5.4.48 || ^6.4.45 || ^7.4.18 || ^8.1.7", + "symfony/yaml": "^5.4.53 || ^6.4.45 || ^7.4.18 || ^8.1.6" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -1609,7 +1414,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.27" }, "funding": [ { @@ -1617,7 +1422,7 @@ "type": "github" } ], - "time": "2026-02-20T16:13:53+00:00" + "time": "2026-09-22T09:54:55+00:00" }, { "name": "kint-php/kint", @@ -1684,74 +1489,22 @@ }, "time": "2024-04-26T14:20:09+00:00" }, - { - "name": "mikey179/vfsstream", - "version": "v1.6.12", - "source": { - "type": "git", - "url": "https://github.com/bovigo/vfsStream.git", - "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/fe695ec993e0a55c3abdda10a9364eb31c6f1bf0", - "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0", - "shasum": "" - }, - "require": { - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.5||^8.5||^9.6", - "yoast/phpunit-polyfills": "^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-0": { - "org\\bovigo\\vfs\\": "src/main/php" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Frank Kleine", - "homepage": "http://frankkleine.de/", - "role": "Developer" - } - ], - "description": "Virtual file system to mock the real file system in unit tests.", - "homepage": "http://vfs.bovigo.org/", - "support": { - "issues": "https://github.com/bovigo/vfsStream/issues", - "source": "https://github.com/bovigo/vfsStream/tree/master", - "wiki": "https://github.com/bovigo/vfsStream/wiki" - }, - "time": "2024-08-29T18:43:31+00:00" - }, { "name": "myclabs/deep-copy", - "version": "1.13.4", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", @@ -1786,42 +1539,42 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/mnapoli", + "type": "github" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-08-11T10:17:44+00:00" }, { "name": "nexusphp/cs-config", - "version": "v3.28.1", + "version": "v3.29.0", "source": { "type": "git", "url": "https://github.com/NexusPHP/cs-config.git", - "reference": "4175b75a053b35dc64f37727df526520efb456f8" + "reference": "0fa9d490cf8b2867a83704a6b29ed2ea01eb7084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/NexusPHP/cs-config/zipball/4175b75a053b35dc64f37727df526520efb456f8", - "reference": "4175b75a053b35dc64f37727df526520efb456f8", + "url": "https://api.github.com/repos/NexusPHP/cs-config/zipball/0fa9d490cf8b2867a83704a6b29ed2ea01eb7084", + "reference": "0fa9d490cf8b2867a83704a6b29ed2ea01eb7084", "shasum": "" }, "require": { "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.94", + "friendsofphp/php-cs-fixer": "^3.95", "php": "^8.2" }, "require-dev": { "nexusphp/tachycardia": "^2.4", "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", + "phpstan/phpstan": "^2.2", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^11.5 || ^12.5" + "phpunit/phpunit": "^11.5 || ^12.5 || ^13.0" }, "type": "library", "autoload": { @@ -1845,24 +1598,23 @@ "slack": "https://nexusphp.slack.com", "source": "https://github.com/NexusPHP/cs-config.git" }, - "time": "2026-02-22T12:03:01+00:00" + "time": "2026-08-22T13:21:04+00:00" }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.9.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -1901,9 +1653,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-09-13T18:51:52+00:00" }, { "name": "phar-io/manifest", @@ -2372,26 +2124,26 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.55", + "version": "11.5.57", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" + "reference": "36b75bf56a8535623f5b3e24b27ea1823759da1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/36b75bf56a8535623f5b3e24b27ea1823759da1a", + "reference": "36b75bf56a8535623f5b3e24b27ea1823759da1a", "shasum": "" }, "require": { "ext-dom": "*", + "ext-filter": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", + "myclabs/deep-copy": "^1.14.0", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", @@ -2454,93 +2206,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.57" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2026-02-18T12:37:06+00:00" - }, - { - "name": "predis/predis", - "version": "v2.4.1", - "source": { - "type": "git", - "url": "https://github.com/predis/predis.git", - "reference": "07105e050622ed80bd60808367ced9e379f31530" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/predis/predis/zipball/07105e050622ed80bd60808367ced9e379f31530", - "reference": "07105e050622ed80bd60808367ced9e379f31530", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.3", - "phpstan/phpstan": "^1.9", - "phpunit/phpcov": "^6.0 || ^8.0", - "phpunit/phpunit": "^8.0 || ^9.4" - }, - "suggest": { - "ext-relay": "Faster connection with in-memory caching (>=0.6.2)" - }, - "type": "library", - "autoload": { - "psr-4": { - "Predis\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Till Krüss", - "homepage": "https://till.im", - "role": "Maintainer" - } - ], - "description": "A flexible and feature-complete Redis/Valkey client for PHP.", - "homepage": "http://github.com/predis/predis", - "keywords": [ - "nosql", - "predis", - "redis" - ], - "support": { - "issues": "https://github.com/predis/predis/issues", - "source": "https://github.com/predis/predis/tree/v2.4.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/tillkruss", - "type": "github" - } - ], - "time": "2025-11-12T18:00:11+00:00" + "time": "2026-10-05T05:47:54+00:00" }, { "name": "psr/container", @@ -4211,16 +3885,16 @@ }, { "name": "symfony/console", - "version": "v7.4.7", + "version": "v7.4.21", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + "reference": "21f1bc2296f7ff6043241a8dcbc09559b3732820" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "url": "https://api.github.com/repos/symfony/console/zipball/21f1bc2296f7ff6043241a8dcbc09559b3732820", + "reference": "21f1bc2296f7ff6043241a8dcbc09559b3732820", "shasum": "" }, "require": { @@ -4285,7 +3959,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.7" + "source": "https://github.com/symfony/console/tree/v7.4.21" }, "funding": [ { @@ -4305,20 +3979,20 @@ "type": "tidelift" } ], - "time": "2026-03-06T14:06:20+00:00" + "time": "2026-10-12T18:45:38+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -4331,7 +4005,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4356,7 +4030,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -4367,25 +4041,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.4", + "version": "v7.4.17", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "dc2c0eba1af673e736bb851d747d266108aea746" + "reference": "d269974ee93c61d03620ffee358355bfdb471d66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dc2c0eba1af673e736bb851d747d266108aea746", - "reference": "dc2c0eba1af673e736bb851d747d266108aea746", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d269974ee93c61d03620ffee358355bfdb471d66", + "reference": "d269974ee93c61d03620ffee358355bfdb471d66", "shasum": "" }, "require": { @@ -4437,7 +4115,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/v7.4.4" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.17" }, "funding": [ { @@ -4457,20 +4135,20 @@ "type": "tidelift" } ], - "time": "2026-01-05T11:45:34+00:00" + "time": "2026-08-21T17:40:08+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", "shasum": "" }, "require": { @@ -4484,7 +4162,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4517,7 +4195,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1" }, "funding": [ { @@ -4528,25 +4206,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/filesystem", - "version": "v7.4.6", + "version": "v7.4.21", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e" + "reference": "fd0e3462be6d32a0d130e8423e4940a7688162d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3ebc794fa5315e59fd122561623c2e2e4280538e", - "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd0e3462be6d32a0d130e8423e4940a7688162d8", + "reference": "fd0e3462be6d32a0d130e8423e4940a7688162d8", "shasum": "" }, "require": { @@ -4583,7 +4265,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.6" + "source": "https://github.com/symfony/filesystem/tree/v7.4.21" }, "funding": [ { @@ -4603,20 +4285,20 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:50:00+00:00" + "time": "2026-10-06T20:05:19+00:00" }, { "name": "symfony/finder", - "version": "v7.4.6", + "version": "v7.4.20", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf" + "reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8655bf1076b7a3a346cb11413ffdabff50c7ffcf", - "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "url": "https://api.github.com/repos/symfony/finder/zipball/2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e", + "reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e", "shasum": "" }, "require": { @@ -4651,7 +4333,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.6" + "source": "https://github.com/symfony/finder/tree/v7.4.20" }, "funding": [ { @@ -4671,20 +4353,20 @@ "type": "tidelift" } ], - "time": "2026-01-29T09:40:50+00:00" + "time": "2026-09-26T06:31:10+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "b38026df55197f9e39a44f3215788edf83187b80" + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", - "reference": "b38026df55197f9e39a44f3215788edf83187b80", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", "shasum": "" }, "require": { @@ -4722,7 +4404,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.4.8" }, "funding": [ { @@ -4742,20 +4424,20 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -4805,7 +4487,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -4825,20 +4507,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5cac8f4db97208823aa353f59a01faad2da52c75", + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75", "shasum": "" }, "require": { @@ -4887,7 +4569,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.0" }, "funding": [ { @@ -4907,20 +4589,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-09-25T15:46:47+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b", "shasum": "" }, "require": { @@ -4972,7 +4654,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0" }, "funding": [ { @@ -4992,20 +4674,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-09-23T22:02:09+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", "shasum": "" }, "require": { @@ -5057,7 +4739,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0" }, "funding": [ { @@ -5077,20 +4759,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-09-24T15:19:04+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", "shasum": "" }, "require": { @@ -5141,7 +4823,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.43.0" }, "funding": [ { @@ -5161,20 +4843,20 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-09-25T15:50:05+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", "shasum": "" }, "require": { @@ -5221,7 +4903,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.38.1" }, "funding": [ { @@ -5241,20 +4923,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.33.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015", "shasum": "" }, "require": { @@ -5301,7 +4983,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0" }, "funding": [ { @@ -5321,20 +5003,20 @@ "type": "tidelift" } ], - "time": "2025-06-24T13:30:11+00:00" + "time": "2026-09-23T22:01:09+00:00" }, { "name": "symfony/process", - "version": "v7.4.5", + "version": "v7.4.21", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "608476f4604102976d687c483ac63a79ba18cc97" + "reference": "e5247f4a5e0797d232ad8e8e6803aeb341843c83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", - "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "url": "https://api.github.com/repos/symfony/process/zipball/e5247f4a5e0797d232ad8e8e6803aeb341843c83", + "reference": "e5247f4a5e0797d232ad8e8e6803aeb341843c83", "shasum": "" }, "require": { @@ -5366,7 +5048,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.5" + "source": "https://github.com/symfony/process/tree/v7.4.21" }, "funding": [ { @@ -5386,20 +5068,20 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:07:59+00:00" + "time": "2026-10-08T16:56:12+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.3", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", "shasum": "" }, "require": { @@ -5417,7 +5099,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -5453,7 +5135,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.3" }, "funding": [ { @@ -5473,20 +5155,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-07-27T15:39:01+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "8a24af0a2e8a872fb745047180649b8418303084" + "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8a24af0a2e8a872fb745047180649b8418303084", - "reference": "8a24af0a2e8a872fb745047180649b8418303084", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/70a852d72fec4d51efb1f48dcd968efcaf5ccb89", + "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89", "shasum": "" }, "require": { @@ -5519,7 +5201,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.4.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.4.8" }, "funding": [ { @@ -5539,20 +5221,20 @@ "type": "tidelift" } ], - "time": "2025-08-04T07:05:15+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/string", - "version": "v7.4.6", + "version": "v7.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "9f209231affa85aa930a5e46e6eb03381424b30b" + "reference": "9bc35d074ad16a58c21cd52b18c5cacc4f4c3981" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/9f209231affa85aa930a5e46e6eb03381424b30b", - "reference": "9f209231affa85aa930a5e46e6eb03381424b30b", + "url": "https://api.github.com/repos/symfony/string/zipball/9bc35d074ad16a58c21cd52b18c5cacc4f4c3981", + "reference": "9bc35d074ad16a58c21cd52b18c5cacc4f4c3981", "shasum": "" }, "require": { @@ -5610,7 +5292,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.6" + "source": "https://github.com/symfony/string/tree/v7.4.21" }, "funding": [ { @@ -5630,7 +5312,7 @@ "type": "tidelift" } ], - "time": "2026-02-09T09:33:46+00:00" + "time": "2026-10-03T15:15:30+00:00" }, { "name": "theseer/tokenizer", @@ -5689,7 +5371,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.2", + "php": ">=8.2.35 <8.3 || >=8.3.36 <8.4 || >=8.4.27", "ext-intl": "*" }, "platform-dev": {}, diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 856ede37f..8b41f5ed1 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,6 +1,6 @@ ## Server Requirements -- PHP version `8.2` to `8.4` are supported, PHP version `≤ 8.1` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl` and `php-xml` installed and enabled. An unstable master build can be downloaded in the releases section. +- PHP version `8.2.35` to `8.4.27` are supported (minimum patch levels required for CVE-2026-109618 / CVE-2026-109619 security fixes). PHP version `≤ 8.1` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl` and `php-xml` installed and enabled. An unstable master build can be downloaded in the releases section. - MySQL `5.7` is supported, also MariaDB replacement `10.x` is supported and might offer better performance. - Apache `2.4` is supported. Nginx should work fine too, see [wiki page here](https://github.com/opensourcepos/opensourcepos/wiki/Local-Deployment-using-LEMP). - Raspberry PI based installations proved to work, see [wiki page here](). diff --git a/package-lock.json b/package-lock.json index cda06b03a..96f2e8361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@opensourcepos/opensourcepos", - "version": "3.4.3", + "version": "3.4.4-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@opensourcepos/opensourcepos", - "version": "3.4.3", + "version": "3.4.4-dev", "license": "MIT", "dependencies": { "bootstrap": "^3.4.1", diff --git a/package.json b/package.json index 834c46159..1a53bf713 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@opensourcepos/opensourcepos", - "version": "3.4.3", + "version": "3.4.4-dev", "description": "Open Source Point of Sale is a web based point of sale system written in the PHP language. It uses MySQL as the data storage back-end and has a simple user interface.", "keywords": [ "point-of-sale", diff --git a/tests/Config/Validation/OSPOSRulesTest.php b/tests/Config/Validation/OSPOSRulesTest.php index 6371db6bd..d3c9fa748 100644 --- a/tests/Config/Validation/OSPOSRulesTest.php +++ b/tests/Config/Validation/OSPOSRulesTest.php @@ -7,9 +7,9 @@ use App\Models\Employee; use CodeIgniter\Config\Factories; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\Test\CIUnitTestCase; -use Config\OSPOS; use Config\Services; use PHPUnit\Framework\MockObject\MockObject; +use Tests\Support\OsposConfigFixtureTrait; /** * Regression tests for the login validation flow in OSPOSRules::login_check(). @@ -19,6 +19,8 @@ use PHPUnit\Framework\MockObject\MockObject; */ class OSPOSRulesTest extends CIUnitTestCase { + use OsposConfigFixtureTrait; + protected function tearDown(): void { Factories::reset(); @@ -138,9 +140,7 @@ class OSPOSRulesTest extends CIUnitTestCase private function injectSettings(array $settings): void { - $ospos = new OSPOS(); - $ospos->settings = $settings; - Factories::injectMock('config', OSPOS::class, $ospos); + $this->injectOsposSettings($settings); } private function injectRequest(): void diff --git a/tests/Controllers/CashupControllerTest.php b/tests/Controllers/CashupControllerTest.php index 5db565f6b..023eed9eb 100644 --- a/tests/Controllers/CashupControllerTest.php +++ b/tests/Controllers/CashupControllerTest.php @@ -3,22 +3,23 @@ namespace Tests\Controllers; use App\Models\Cashup; -use CodeIgniter\Config\Factories; -use CodeIgniter\Database\Config; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; -use Config\OSPOS; use ReflectionProperty; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\EmployeeFixtureTrait; +use Tests\Support\OsposConfigFixtureTrait; class CashupControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; use EmployeeFixtureTrait; + use OsposConfigFixtureTrait; protected $migrate = true; protected $migrateOnce = true; @@ -26,34 +27,24 @@ class CashupControllerTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); - $ospos = new OSPOS(); - $ospos->settings = [ + $this->injectOsposSettings([ 'company' => 'Test Co', 'dateformat' => 'Y-m-d', 'timeformat' => 'H:i:s', 'number_locale' => 'en_US', 'currency_decimals' => 2, 'thousands_separator' => ',', - ]; - Factories::injectMock('config', OSPOS::class, $ospos); + ]); } protected function tearDown(): void { - Factories::reset(); parent::tearDown(); } diff --git a/tests/Controllers/CustomersCsvImportTest.php b/tests/Controllers/CustomersCsvImportTest.php index fc30a1288..18f0f6429 100644 --- a/tests/Controllers/CustomersCsvImportTest.php +++ b/tests/Controllers/CustomersCsvImportTest.php @@ -2,16 +2,17 @@ namespace Tests\Controllers; -use CodeIgniter\Database\Config; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; use App\Models\Customer; use App\Models\Employee; +use Tests\Support\DatabaseBootstrapTrait; class CustomersCsvImportTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; protected $migrate = true; @@ -22,19 +23,12 @@ class CustomersCsvImportTest extends CIUnitTestCase protected Customer $customer; protected Employee $employee; - private static bool $doneBootstrap = false; - protected function setUp(): void { // Reset the test database to a clean schema on the first test in this // class so leftover customers with the same emails from prior runs // cannot be rejected as duplicates by the import (check_email_exists). - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); // Reset any stale transaction state left on the shared in-process DB diff --git a/tests/Controllers/EmployeesControllerTest.php b/tests/Controllers/EmployeesControllerTest.php index 380d37ec2..b0727c72e 100644 --- a/tests/Controllers/EmployeesControllerTest.php +++ b/tests/Controllers/EmployeesControllerTest.php @@ -2,7 +2,6 @@ namespace Tests\Controllers; -use CodeIgniter\Database\Config; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Test\CIUnitTestCase; @@ -12,11 +11,13 @@ use CodeIgniter\Config\Services; use App\Models\Employee; use App\Models\Module; use ReflectionProperty; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\EmployeeFixtureTrait; class EmployeesControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; use EmployeeFixtureTrait; @@ -27,20 +28,13 @@ class EmployeesControllerTest extends CIUnitTestCase protected $priorDisallowGrantChange; - private static bool $doneBootstrap = false; - protected function setUp(): void { // Reset the test database to a known clean schema on the first test in // this class so stale employees from prior runs (whose usernames are // unique-key bound and whose grant sets leak into these assertions) // cannot contaminate the assertions below. - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); // Reset any stale transaction state left on the shared in-process DB diff --git a/tests/Controllers/HomeTest.php b/tests/Controllers/HomeTest.php index d10c8c650..3667b80f5 100644 --- a/tests/Controllers/HomeTest.php +++ b/tests/Controllers/HomeTest.php @@ -2,12 +2,12 @@ namespace Tests\Controllers; -use CodeIgniter\Database\Config; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; use App\Models\Employee; use RuntimeException; +use Tests\Support\DatabaseBootstrapTrait; /** * Test suite for Home controller password validation @@ -19,6 +19,7 @@ use RuntimeException; class HomeTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; protected $migrate = true; @@ -26,16 +27,9 @@ class HomeTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); } diff --git a/tests/Controllers/ItemKitsControllerTest.php b/tests/Controllers/ItemKitsControllerTest.php index 5ea6382ce..18f84aa67 100644 --- a/tests/Controllers/ItemKitsControllerTest.php +++ b/tests/Controllers/ItemKitsControllerTest.php @@ -2,8 +2,6 @@ namespace Tests\Controllers; -use CodeIgniter\Config\Factories; -use CodeIgniter\Database\Config; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Test\CIUnitTestCase; @@ -11,14 +9,17 @@ use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; use App\Models\Item; use App\Models\Item_kit; -use Config\OSPOS; use Exception; use ReflectionProperty; +use Tests\Support\DatabaseBootstrapTrait; +use Tests\Support\OsposConfigFixtureTrait; class ItemKitsControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; + use OsposConfigFixtureTrait; protected $migrate = true; protected $migrateOnce = true; @@ -26,24 +27,16 @@ class ItemKitsControllerTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - protected Item $item; protected Item_kit $itemKit; protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); - $ospos = new OSPOS(); - $ospos->settings = [ + $this->injectOsposSettings([ 'company' => 'Test Co', 'barcode_content' => 'id', 'barcode_type' => 'C128', @@ -59,8 +52,7 @@ class ItemKitsControllerTest extends CIUnitTestCase 'barcode_page_cellspacing' => 1, 'barcode_generate_if_empty' => 0, 'barcode_formats' => 'null', - ]; - Factories::injectMock('config', OSPOS::class, $ospos); + ]); $this->item = model(Item::class); $this->itemKit = model(Item_kit::class); @@ -68,7 +60,6 @@ class ItemKitsControllerTest extends CIUnitTestCase protected function tearDown(): void { - Factories::reset(); parent::tearDown(); } diff --git a/tests/Controllers/ReportsControllerTest.php b/tests/Controllers/ReportsControllerTest.php index 372fc6ba0..714142cc2 100644 --- a/tests/Controllers/ReportsControllerTest.php +++ b/tests/Controllers/ReportsControllerTest.php @@ -6,9 +6,9 @@ use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; -use App\Database\Seeds\TestDatabaseBootstrapSeeder; use App\Models\Employee; use Config\OSPOS; +use Tests\Support\DatabaseBootstrapTrait; /** * Regression tests for the reports permission bypass @@ -24,6 +24,7 @@ use Config\OSPOS; class ReportsControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; protected $migrate = true; @@ -31,18 +32,12 @@ class ReportsControllerTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - /** * Set up test environment */ protected function setUp(): void { - if (self::$doneBootstrap === false) { - TestDatabaseBootstrapSeeder::reset(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); diff --git a/tests/Controllers/SalesControllerTest.php b/tests/Controllers/SalesControllerTest.php index 9bfdd9bb0..cdd82e151 100644 --- a/tests/Controllers/SalesControllerTest.php +++ b/tests/Controllers/SalesControllerTest.php @@ -2,7 +2,6 @@ namespace Tests\Controllers; -use CodeIgniter\Database\Config; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Test\CIUnitTestCase; @@ -12,6 +11,7 @@ use CodeIgniter\Config\Services; use App\Models\Employee; use Config\Database; use ReflectionProperty; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\ItemFixtureTrait; use Tests\Support\EmployeeFixtureTrait; use Tests\Support\SaleFixtureTrait; @@ -31,6 +31,7 @@ use Tests\Support\SaleFixtureTrait; class SalesControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; use ItemFixtureTrait; use EmployeeFixtureTrait; @@ -42,16 +43,9 @@ class SalesControllerTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); } diff --git a/tests/Controllers/TaxesControllerTest.php b/tests/Controllers/TaxesControllerTest.php index 8c673ea50..23ec9c8c7 100644 --- a/tests/Controllers/TaxesControllerTest.php +++ b/tests/Controllers/TaxesControllerTest.php @@ -2,7 +2,6 @@ namespace Tests\Controllers; -use CodeIgniter\Database\Config; use CodeIgniter\HTTP\DownloadResponse; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Test\CIUnitTestCase; @@ -10,11 +9,13 @@ use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; use App\Models\Tax; use ReflectionProperty; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\EmployeeFixtureTrait; class TaxesControllerTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use FeatureTestTrait; use EmployeeFixtureTrait; @@ -24,16 +25,9 @@ class TaxesControllerTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static $doneBootstrap = false; - protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); } diff --git a/tests/helpers/GetItemDataRowTest.php b/tests/Helpers/GetItemDataRowTest.php similarity index 92% rename from tests/helpers/GetItemDataRowTest.php rename to tests/Helpers/GetItemDataRowTest.php index 89ae14729..6cdf7b152 100644 --- a/tests/helpers/GetItemDataRowTest.php +++ b/tests/Helpers/GetItemDataRowTest.php @@ -2,13 +2,14 @@ namespace Tests\Helpers; -use CodeIgniter\Config\Factories; use CodeIgniter\Test\CIUnitTestCase; -use Config\OSPOS; use stdClass; +use Tests\Support\OsposConfigFixtureTrait; class GetItemDataRowTest extends CIUnitTestCase { + use OsposConfigFixtureTrait; + protected function setUp(): void { parent::setUp(); @@ -20,8 +21,7 @@ class GetItemDataRowTest extends CIUnitTestCase protected function injectSettings(array $overrides = []): void { - $config = new OSPOS(); - $config->settings = array_merge([ + $this->injectOsposSettings(array_merge([ 'multi_pack_enabled' => 0, 'use_destination_based_tax' => 0, 'number_locale' => 'en_US', @@ -29,9 +29,7 @@ class GetItemDataRowTest extends CIUnitTestCase 'quantity_decimals' => 0, 'thousands_separator' => 1, 'currency_symbol' => '$', - ], $overrides); - - Factories::injectMock('config', OSPOS::class, $config); + ], $overrides)); } protected function makeItem(array $overrides = []): stdClass diff --git a/tests/helpers/LocaleHelperTest.php b/tests/Helpers/LocaleHelperTest.php similarity index 86% rename from tests/helpers/LocaleHelperTest.php rename to tests/Helpers/LocaleHelperTest.php index a76dcf00c..b82aa55a8 100644 --- a/tests/helpers/LocaleHelperTest.php +++ b/tests/Helpers/LocaleHelperTest.php @@ -1,19 +1,20 @@ settings = ['dateformat' => 'Y-m-d']; - Factories::injectMock('config', OSPOS::class, $config); + $this->injectOsposSettings(['dateformat' => 'Y-m-d']); } public function testValidDateReturnsTrue(): void diff --git a/tests/Helpers/UrlHelperTest.php b/tests/Helpers/UrlHelperTest.php new file mode 100644 index 000000000..096eb6a5b --- /dev/null +++ b/tests/Helpers/UrlHelperTest.php @@ -0,0 +1,27 @@ +assertMatchesRegularExpression('/^[A-Za-z0-9\-_]+$/', $encoded); + + // Assert that decoding the encoded string returns the original data + $decoded = base64url_decode($encoded); + $this->assertEquals($data, $decoded); + } +} diff --git a/tests/helpers/export_helperTest.php b/tests/Helpers/export_helperTest.php similarity index 100% rename from tests/helpers/export_helperTest.php rename to tests/Helpers/export_helperTest.php diff --git a/tests/helpers/migration_helperTest.php b/tests/Helpers/migration_helperTest.php similarity index 98% rename from tests/helpers/migration_helperTest.php rename to tests/Helpers/migration_helperTest.php index 5fe2e5555..4e1e3b5e4 100644 --- a/tests/helpers/migration_helperTest.php +++ b/tests/Helpers/migration_helperTest.php @@ -1,6 +1,6 @@ settings = [ + $this->injectOsposSettings([ 'cash_rounding_code' => '', 'cash_decimals' => 2, 'currency_decimals' => 2, 'tax_decimals' => 2, 'quantity_decimals' => 2, - ]; - Factories::injectMock('config', OSPOS::class, $ospos); + ]); // Inject stub models so model() calls in Sale_lib::__construct() skip DB $stubMethods = ['__construct']; diff --git a/tests/Models/AppconfigTest.php b/tests/Models/AppconfigTest.php index 566ee4163..12ca2e89b 100644 --- a/tests/Models/AppconfigTest.php +++ b/tests/Models/AppconfigTest.php @@ -5,12 +5,13 @@ namespace Tests\Models; use App\Models\Appconfig; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; -use Config\Database; use Config\OSPOS; +use Tests\Support\DatabaseBootstrapTrait; class AppconfigTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; protected $migrate = true; protected $migrateOnce = true; @@ -19,10 +20,11 @@ class AppconfigTest extends CIUnitTestCase protected $refresh = true; protected $namespace = null; - public static function setUpBeforeClass(): void + protected function setUp(): void { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); + $this->resetDatabaseOnce(); + + parent::setUp(); } private function appconfig(): Appconfig diff --git a/tests/Models/CustomerRewardPointsTest.php b/tests/Models/CustomerRewardPointsTest.php index 7e89a29d4..3e0be4669 100644 --- a/tests/Models/CustomerRewardPointsTest.php +++ b/tests/Models/CustomerRewardPointsTest.php @@ -5,8 +5,8 @@ namespace Tests\Models; use App\Models\Customer; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; -use Config\Database; use Tests\Support\ConcurrentDbRaceTrait; +use Tests\Support\DatabaseBootstrapTrait; /** * Regression tests: adjustRewardPoints() must apply @@ -17,6 +17,7 @@ use Tests\Support\ConcurrentDbRaceTrait; class CustomerRewardPointsTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use ConcurrentDbRaceTrait; protected $migrate = true; @@ -24,14 +25,10 @@ class CustomerRewardPointsTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - public static function setUpBeforeClass(): void - { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); - } - protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); } diff --git a/tests/Models/EmployeeTest.php b/tests/Models/EmployeeTest.php index 4ff2f05a8..9fb305a93 100644 --- a/tests/Models/EmployeeTest.php +++ b/tests/Models/EmployeeTest.php @@ -5,20 +5,25 @@ namespace Tests\Models; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use App\Models\Employee; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\EmployeeFixtureTrait; class EmployeeTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use EmployeeFixtureTrait; protected $migrate = true; protected $migrateOnce = true; + protected $seedOnce = true; protected $refresh = false; protected $namespace = null; protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); } diff --git a/tests/Models/GiftcardTest.php b/tests/Models/GiftcardTest.php index d097cd673..064996058 100644 --- a/tests/Models/GiftcardTest.php +++ b/tests/Models/GiftcardTest.php @@ -5,8 +5,8 @@ namespace Tests\Models; use App\Models\Giftcard; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; -use Config\Database; use Tests\Support\ConcurrentDbRaceTrait; +use Tests\Support\DatabaseBootstrapTrait; /** * Regression tests: decrementGiftcardValue() must @@ -17,6 +17,7 @@ use Tests\Support\ConcurrentDbRaceTrait; class GiftcardTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use ConcurrentDbRaceTrait; protected $migrate = true; @@ -24,14 +25,10 @@ class GiftcardTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - public static function setUpBeforeClass(): void - { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); - } - protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); } diff --git a/tests/Models/ItemBulkUpdateTest.php b/tests/Models/ItemBulkUpdateTest.php index 4e1cdcb5a..7ce3733f7 100644 --- a/tests/Models/ItemBulkUpdateTest.php +++ b/tests/Models/ItemBulkUpdateTest.php @@ -5,6 +5,7 @@ namespace Tests\Models; use App\Models\Item; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; +use Tests\Support\DatabaseBootstrapTrait; /** * Regression coverage for mass assignment in bulk edit. @@ -15,15 +16,13 @@ use CodeIgniter\Test\DatabaseTestTrait; class ItemBulkUpdateTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; - /** - * The schema is taken as it stands rather than migrated, so these tests run - * against an existing test database without dropping it. Every row created - * here is rolled back in tearDown(). - */ - protected $migrate = false; - protected $refresh = false; - protected $namespace = null; + protected $migrate = true; + protected $migrateOnce = true; + protected $seedOnce = true; + protected $refresh = false; + protected $namespace = null; protected Item $item; @@ -35,6 +34,8 @@ class ItemBulkUpdateTest extends CIUnitTestCase protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); $this->item = model(Item::class); diff --git a/tests/Models/ItemQuantityTest.php b/tests/Models/ItemQuantityTest.php index 8ca38a267..8fa3e037b 100644 --- a/tests/Models/ItemQuantityTest.php +++ b/tests/Models/ItemQuantityTest.php @@ -5,8 +5,8 @@ namespace Tests\Models; use App\Models\Item_quantity; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; -use Config\Database; use Tests\Support\ConcurrentDbRaceTrait; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\ItemFixtureTrait; /** @@ -20,6 +20,7 @@ use Tests\Support\ItemFixtureTrait; class ItemQuantityTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use ConcurrentDbRaceTrait; use ItemFixtureTrait; @@ -30,14 +31,10 @@ class ItemQuantityTest extends CIUnitTestCase private const LOCATION_ID = 1; - public static function setUpBeforeClass(): void - { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); - } - protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); } diff --git a/tests/Models/ItemSearchTest.php b/tests/Models/ItemSearchTest.php index 24f60826b..0aaea591e 100644 --- a/tests/Models/ItemSearchTest.php +++ b/tests/Models/ItemSearchTest.php @@ -5,13 +5,14 @@ namespace Tests\Models; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use App\Models\Item; -use Config\Database; use Config\OSPOS; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\ItemSearchFixtureTrait; class ItemSearchTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use ItemSearchFixtureTrait; protected $migrate = true; @@ -23,14 +24,10 @@ class ItemSearchTest extends CIUnitTestCase protected $item; - public static function setUpBeforeClass(): void - { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); - } - protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); $this->item = model(Item::class); diff --git a/tests/Models/ItemTaxesMultipleTest.php b/tests/Models/ItemTaxesMultipleTest.php index 1c0372b67..301453123 100644 --- a/tests/Models/ItemTaxesMultipleTest.php +++ b/tests/Models/ItemTaxesMultipleTest.php @@ -5,12 +5,13 @@ namespace Tests\Models; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use App\Models\Item_taxes; -use Config\Database; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\ItemSearchFixtureTrait; class ItemTaxesMultipleTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use ItemSearchFixtureTrait; protected $migrate = true; @@ -22,14 +23,10 @@ class ItemTaxesMultipleTest extends CIUnitTestCase protected $item_taxes; - public static function setUpBeforeClass(): void - { - $seeder = Database::seeder('tests'); - $seeder->call('TestDatabaseBootstrapSeeder'); - } - protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); $this->item_taxes = model(Item_taxes::class); diff --git a/tests/Models/Reports/Summary_taxes_test.php b/tests/Models/Reports/Summary_taxes_test.php index ddbb43cb5..7c798054e 100644 --- a/tests/Models/Reports/Summary_taxes_test.php +++ b/tests/Models/Reports/Summary_taxes_test.php @@ -7,13 +7,16 @@ namespace Tests\Models\Reports; use App\Models\Reports\Summary_taxes; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; +use Tests\Support\DatabaseBootstrapTrait; class Summary_taxes_test extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; protected $migrate = true; protected $migrateOnce = true; + protected $seedOnce = true; protected $refresh = false; protected $namespace = null; @@ -22,6 +25,8 @@ class Summary_taxes_test extends CIUnitTestCase protected function setUp(): void { + $this->resetDatabaseOnce(); + parent::setUp(); $this->seedTestData(); } diff --git a/tests/Models/SaleTest.php b/tests/Models/SaleTest.php index e6372d754..f2efedc80 100644 --- a/tests/Models/SaleTest.php +++ b/tests/Models/SaleTest.php @@ -3,9 +3,9 @@ namespace Tests\Models; use App\Models\Sale; -use CodeIgniter\Database\Config; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; +use Tests\Support\DatabaseBootstrapTrait; use Tests\Support\EmployeeFixtureTrait; use Tests\Support\FailedQueryConnectionTrait; use Tests\Support\ItemFixtureTrait; @@ -18,6 +18,7 @@ use Tests\Support\ItemFixtureTrait; class SaleTest extends CIUnitTestCase { use DatabaseTestTrait; + use DatabaseBootstrapTrait; use EmployeeFixtureTrait; use FailedQueryConnectionTrait; use ItemFixtureTrait; @@ -28,18 +29,11 @@ class SaleTest extends CIUnitTestCase protected $refresh = false; protected $namespace = null; - private static bool $doneBootstrap = false; - private const LOCATION_ID = 1; protected function setUp(): void { - if (self::$doneBootstrap === false) { - Config::seeder($this->DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); - Config::connect($this->DBGroup)->close(); - - self::$doneBootstrap = true; - } + $this->resetDatabaseOnce(); parent::setUp(); } diff --git a/tests/Support/DatabaseBootstrapTrait.php b/tests/Support/DatabaseBootstrapTrait.php new file mode 100644 index 000000000..12ad6d15a --- /dev/null +++ b/tests/Support/DatabaseBootstrapTrait.php @@ -0,0 +1,50 @@ +resetDatabaseOnce(); + * + * parent::setUp(); + * } + * } + */ +trait DatabaseBootstrapTrait +{ + private static bool $doneBootstrap = false; + + protected function resetDatabaseOnce(): void + { + if (self::$doneBootstrap === true) { + return; + } + + TestDatabaseBootstrapSeeder::reset(); + Config::connect($this->DBGroup)->close(); + + self::$doneBootstrap = true; + } +} diff --git a/tests/Support/OsposConfigFixtureTrait.php b/tests/Support/OsposConfigFixtureTrait.php new file mode 100644 index 000000000..a6e1679ec --- /dev/null +++ b/tests/Support/OsposConfigFixtureTrait.php @@ -0,0 +1,50 @@ +settings = [...]; + * Factories::injectMock(...)` block that was duplicated with slightly + * different settings arrays across many test files. Each test supplies the + * exact settings it depends on - OSPOS::getDefaultSettings() is private and + * only has a handful of keys, so it is not a usable base to merge onto. + * + * Usage: + * + * use Tests\Support\OsposConfigFixtureTrait; + * + * class FooTest extends CIUnitTestCase + * { + * use OsposConfigFixtureTrait; + * + * protected function setUp(): void + * { + * parent::setUp(); + * + * $this->injectOsposSettings([ + * 'invoice_enable' => false, + * 'number_locale' => 'en_US', + * ]); + * } + * } + */ +trait OsposConfigFixtureTrait +{ + /** + * @param array $settings + */ + protected function injectOsposSettings(array $settings): OSPOS + { + $ospos = new OSPOS(); + $ospos->settings = $settings; + + Factories::injectMock('config', OSPOS::class, $ospos); + + return $ospos; + } +} diff --git a/tests/helpers/UrlHelperTest.php b/tests/helpers/UrlHelperTest.php deleted file mode 100644 index d49bc279c..000000000 --- a/tests/helpers/UrlHelperTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertMatchesRegularExpression('/^[A-Za-z0-9\-_]+$/', $encoded); - - // Assert that decoding the encoded string returns the original data - $decoded = base64url_decode($encoded); - $this->assertEquals($data, $decoded); - } - }