diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index d122ce7..f213687 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -3,8 +3,12 @@ name: PHP Checks
on:
push:
branches: ["trunk", "develop"]
+ # Deliberately unfiltered. A `branches` list here matches the PR's *base* branch, so
+ # restricting it to trunk/develop silently skipped every PR opened against a release branch
+ # such as feature/2.0.0 - the checks tab simply reported nothing for the commit. Pushes stay
+ # limited to the long-lived branches so PR commits do not run twice.
pull_request:
- branches: ["trunk", "develop"]
+ workflow_dispatch:
permissions:
contents: read
@@ -55,9 +59,36 @@ jobs:
run: composer run static
test:
- name: Unit Tests
+ name: Tests
runs-on: ubuntu-latest
+ # The integration suite runs against a real WordPress, which Mantle installs itself into a
+ # temporary directory. It still needs a database to install into.
+ services:
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
+ MYSQL_DATABASE: wp_framework_tests
+ ports:
+ - 3306:3306
+ options: >-
+ --health-cmd="mysqladmin ping --silent"
+ --health-interval=10s
+ --health-timeout=5s
+ --health-retries=5
+
+ env:
+ # Mantle reads these via getenv(); tests/bootstrap.php only supplies defaults for whatever
+ # is unset, so these win. WP_CORE_DIR is kept per-package so a shared runner can never
+ # hand us another project's cached wp-tests-config.php, which would point the destructive
+ # installer at the wrong database.
+ WP_DB_NAME: wp_framework_tests
+ WP_DB_USER: root
+ WP_DB_PASSWORD: ""
+ WP_DB_HOST: 127.0.0.1
+ WP_CORE_DIR: ${{ github.workspace }}/.wordpress-tests
+
steps:
- uses: actions/checkout@v3
@@ -66,9 +97,10 @@ jobs:
with:
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2
+ extensions: mysqli
- name: Install dependencies
run: composer install --no-progress --no-suggest
- - name: Run PHPUnit
+ - name: Run Pest
run: composer run test
diff --git a/.gitignore b/.gitignore
index 675fea7..62c849c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,9 @@ vendor/
coverage/
.phpunit.result.cache
+# WordPress installed for the integration suite (CI points WP_CORE_DIR here; locally it
+# defaults to the system temp directory).
+.wordpress-tests/
+
# Generated class-loader cache (a build artefact, not source)
class-loader-cache/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8455eb4..ff61275 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file, per [the Ke
- The loader debug page reports per-loader timing: how long class **discovery** took (a cache read when cached, a live filesystem scan otherwise) and how long **class lookup** (reflection, instantiation and registration) took. The staleness check also reports how long its live discovery ran, so the cache's saving on a given site is measurable.
### Changed
+- **Test suite migrated to [Pest 4](https://pestphp.com) + [Mantle Testkit](https://mantle.alley.com/testing/testkit/), replacing PHPUnit 9 and Brain Monkey.** Tests now run against a real WordPress installation and MySQL database instead of mocked WordPress functions, split into `tests/Unit/` (no WordPress), `tests/Integration/` (real WordPress) and `tests/Arch/` (Pest architecture rules). `AbstractPostType` and `AbstractTaxonomy` gain real coverage as a result: mocked `register_post_type()` calls could only prove the function was invoked, so those two classes previously had one test each. Development-only; nothing about the shipped package changes. Contributors need a MySQL database available — see the Testing section of `CLAUDE.md`.
- **Breaking: the minimum required PHP version is now 8.3** (raised from 8.2). Full native type coverage relies on [typed class constants](https://www.php.net/manual/en/language.oop5.constants.php), which are a PHP 8.3 feature and are a parse error on 8.2. This reverses the lowering to 8.2 made in 1.2.0 ([#8](https://github.com/10up/wp-framework/pull/8)). See the [Upgrade Guide](docs/Upgrade-Guide.md).
- **Breaking (static analysis only): `AbstractPostType::get_name()` and `AbstractTaxonomy::get_name()` now document a `lowercase-string&non-empty-string` return type**, stating what WordPress already requires of a post type or taxonomy key. The native `: string` signature is unchanged, so no subclass breaks and no runtime behaviour differs, but a project running PHPStan may need to narrow how it builds a dynamic key. See the [Upgrade Guide](docs/Upgrade-Guide.md).
- Full native type coverage across `src/`: every return type, parameter, property and class constant now declares a native type, and the generic types of every `array` are documented. Enforced by `tomasvotruba/type-coverage` through `composer run static`, which also no longer ignores `missingType.iterableValue`.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..2d588e7
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,91 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What this is
+
+`10up/wp-framework` is a **Composer library** (not a runnable plugin/theme) that other 10up WordPress projects `composer require` and extend. It centralizes shared functionality — a module auto-loading system plus abstract base classes for post types, taxonomies, and asset metadata. Code here ships to many downstream projects, so treat the public API (class/method signatures, the `tenup_framework_module_init__{slug}` action, the `Module` trait contract) as stable; breaking changes require deliberate versioning.
+
+- PHP **8.3+** (typed class constants are used throughout, so 8.2 is a parse error). PSR-4: `TenupFramework\` → `src/`. Test namespaces: `TenupFrameworkTests\` → `tests/`, `TenupFrameworkTestClasses\` → `fixtures/classes/`.
+- Tests run **against a real WordPress install with a MySQL database**, via Pest 4 + Mantle Testkit. Brain Monkey is gone. See *Testing* below.
+
+## Commands
+
+```bash
+composer test # Pest (no coverage driver required)
+composer test-coverage # Pest with coverage; needs Xdebug or PCOV
+composer lint # PHPCS against ./phpcs.xml (10up-Default standard)
+composer lint-fix # PHPCBF auto-fix
+composer static # PHPStan, level 10, 1G memory limit
+
+# Run a subset:
+./vendor/bin/pest tests/Unit # one suite
+./vendor/bin/pest tests/Integration/PostTypes # one directory
+./vendor/bin/pest --filter "registers the post type" # one test by name
+```
+
+CI (`.github/workflows/php.yml`) runs lint, static analysis, and tests on PHP 8.3 for pushes/PRs to `trunk` and `develop`. `develop` is the default/working branch. The test job provisions a MySQL service.
+
+## Architecture
+
+### Module system (the core abstraction)
+
+A "Module" is any class implementing `TenupFramework\ModuleInterface` (and usually `use`ing the `TenupFramework\Module` trait for a default `load_order()` of 10). The interface contract is three methods:
+
+- `load_order(): int` — lower runs first. **No relation to WP hook priority** — it only orders registration among modules. Taxonomies default to `9` so they exist before post types (default `10`) associate with them.
+- `can_register(): bool` — gate registration by context (admin-only, frontend-only, feature flag).
+- `register(): void` — attach hooks/filters here. Keep constructors lightweight.
+
+`ModuleInitialization` (a singleton) drives discovery and registration. Downstream projects bootstrap with:
+
+```php
+ModuleInitialization::instance()->init_classes( YOUR_PLUGIN_INC );
+```
+
+The discovery/init flow in `src/ModuleInitialization.php` is the heart of the library:
+
+1. Scans the given directory with **`spatie/php-structure-discoverer`**.
+2. `withoutChains()` is called deliberately — discovery does **not** resolve inheritance chains. This was an intentional change (see git history "Disable chains"); don't re-enable it without understanding the perf/behavior tradeoff.
+3. Each class is reflected and **skipped** unless it is instantiable AND implements `ModuleInterface`. (This is why abstract bases and plain classes like the `Standalone` fixture are never registered.)
+4. Fires `do_action( 'tenup_framework_module_init__{slug}', $instance )` before each module registers — the extension/observability hook. `{slug}` = FQN with `\` → `-`, passed through `sanitize_title`.
+5. Sorts by `load_order()`, then calls `register()` only when `can_register()` is true. Registered instances are retrievable via `ModuleInitialization::get_module( $fqn )`.
+
+**Class cache (read-only at runtime, build-time generated):** Optional and opt-in. At runtime `get_classes()` reads `{dir}/class-loader-cache/class-loader-cache-v2.php` if it exists (via `ReadOnlyFileDiscoverCacheDriver`, whose `put()`/`forget()` are no-ops and whose constructor does not `mkdir`), and discovers live otherwise — it **never writes**. This is the fix for the stale-cache bug (issue #30): a server can't hold a cache it never wrote. The cache is produced at build time by `ModuleInitialization::generate_cache()`, exposed via `bin/tenup-framework-generate-class-cache
` (composer `bin`) and the `composer generate-class-cache` alias; both run without WordPress. Define `TENUP_FRAMEWORK_DISABLE_CLASS_CACHE = true` to ignore any shipped cache and always discover live. The `CACHE_FILENAME` constant (`...-v2.php`) is the invalidation lever — bumping it makes the runtime ignore caches from older versions. See `docs/Build-and-Deployment.md`.
+
+**Loader debug page (`Debug\LoaderDebug`):** A hidden admin page (`admin.php?page=tenup-framework-loaders`, `manage_options`) that shows the state of every class-loader cache on the site and offers an on-demand live-vs-cache staleness diff. It's **admin-only** — `init_classes()` dispatches a loader record to `LoaderDebug::record()` only behind an `is_admin()` check placed *before* any reference to the class, so `LoaderDebug` never autoloads on the front end. Because a site can run 1..n framework copies (per-package installs, possibly php-scoped), aggregation happens over the fixed-string `tenup_framework_debug_loaders` filter rather than class references — every copy contributes its records, and the page registers once via the `$GLOBALS['tenup_framework_debug_page_registered']` guard. Read-only (never writes); disable via the `tenup_framework_enable_loader_debug` filter or `TENUP_FRAMEWORK_DISABLE_LOADER_DEBUG`. See `docs/Debugging.md`.
+
+### Abstract base classes
+
+- `PostTypes\AbstractPostType` — implements `ModuleInterface` via the `Module` trait. Subclasses define `get_name()`, `get_singular_label()`, `get_plural_label()`, `get_menu_icon()`; `register()` calls `register_post_type()` + `register_taxonomies()` + `after_register()`. Override `get_options()`/`get_editor_supports()`/`get_supported_taxonomies()` to customize.
+- `PostTypes\AbstractCorePostType` — for WP-builtin types (post/page). Labels/icon are no-ops; `register()` only wires taxonomies (the type already exists), and `can_register()` returns true.
+- `Taxonomies\AbstractTaxonomy` — `load_order()` of `9`. Subclasses define name + labels; `get_post_types()` returns `[]` by default because **post types declare their own taxonomies**, not the reverse.
+- `Assets\GetAssetInfo` (trait) — reads `*.asset.php` sidecar files (version + dependencies) emitted by the build. Call `setup_asset_vars( $dist_path, $fallback_version )` first or `get_asset_info()` throws `RuntimeException`. Looks under `dist/js/`, `dist/css/`, then `dist/blocks/`; falls back to the provided version + empty deps when no sidecar exists.
+
+## Testing
+
+**Pest 4 on PHPUnit 12, with Mantle Testkit booting a real WordPress.** `tests/bootstrap.php` calls `\Mantle\Testing\install()`, which downloads and installs WordPress into a temp directory on first run. No WordPress checkout or shell script is needed, but **a MySQL database is required**.
+
+Three suites, bound to base classes in `tests/Pest.php`:
+
+| Directory | Base class | Use for |
+| --- | --- | --- |
+| `tests/Unit/` | `Mantle\Testkit\Unit_Test_Case` | Code that calls no WordPress functions — filesystem, the class cache, subprocess runs |
+| `tests/Integration/` | `Mantle\Testkit\Integration_Test_Case` | Anything touching WordPress. Each test runs in a DB transaction that rolls back |
+| `tests/Arch/` | none | Pest architecture expectations (`arch()`) |
+
+Gotchas worth knowing:
+
+- **Database defaults live in `tests/bootstrap.php`** and are only applied when unset, so CI can override each. It defaults to a database named `wp_framework_tests` and a package-specific `WP_CORE_DIR`, deliberately *not* Mantle's `/tmp/wordpress` + `wordpress_unit_tests` defaults: the installer reuses any `wp-tests-config.php` it finds and then drops/recreates tables, so a shared temp dir will destroy another project's test database. The bootstrap refuses to run if the resolved config does not name the expected database.
+- **Process state is not rolled back.** The DB transaction does not reset the `ModuleInitialization` singleton, `LoaderDebug`'s static records, `BlockRegistrar`'s static registries, registered post types/taxonomies, or the block registry. Use `tenup_reset_framework_state()` / `tenup_reset_block_registrar()` in `beforeEach`, and unregister types in `afterEach`.
+- **`define()` needs a subprocess.** Testkit's `Unit_Test_Case` carries `#[RunTestsInSeparateProcesses]`, but that attribute does **not** survive Pest's generated test classes, so a constant defined in a test leaks into every later test. Tests that must define one shell out to `tests/scripts/`, e.g. `disable-class-cache.php`.
+- **Admin context** comes from Mantle's `Admin_Screen` trait (`uses( Admin_Screen::class )`), which makes `is_admin()` true. `set_current_screen()` is unavailable, since Mantle skips the WP core test suite.
+- **`_doing_it_wrong()` fails the test** unless declared with `$this->setExpectedIncorrectUsage( '...' )`.
+- Shared helpers live in `tests/Helpers.php`, loaded via composer `autoload-dev.files`.
+- `fixtures/classes/` holds sample modules used to exercise discovery — e.g. `Standalone` (no interface, must be skipped), `Loadable/InvalidChildClass` (un-loadable, excluded from PHPStan in `phpstan.neon`). When changing discovery logic, update these fixtures and the assertions in `tests/Integration/ModuleInitializationTest.php`.
+- Coverage is measured against `./src/` only. `phpunit.xml.dist` deliberately declares no `` block — doing so makes PHPUnit enable coverage on every run and hard-fail without Xdebug/PCOV.
+
+## Conventions
+
+- `declare( strict_types = 1 );` on every file (PHPCS enforces it, but not required on the first line).
+- All linted code lives in `src/`, `tests/`, `fixtures/` (per `phpcs.xml`).
+- The `tenup-plugin` text domain in base-class labels is a placeholder inherited by downstream projects.
diff --git a/composer.json b/composer.json
index 61052d3..6c8f6f7 100644
--- a/composer.json
+++ b/composer.json
@@ -30,27 +30,30 @@
"psr-4": {
"TenupFrameworkTests\\": "tests/",
"TenupFrameworkTestClasses\\": "fixtures/classes/"
- }
+ },
+ "files": [
+ "tests/Helpers.php"
+ ]
},
"require": {
"php": ">=8.3",
"spatie/php-structure-discoverer": "^2.2"
},
"require-dev": {
- "phpunit/phpunit": "^9.5",
- "yoast/phpunit-polyfills": "^2.0",
- "brain/monkey": "^2.6",
+ "pestphp/pest": "^4.7",
+ "mantle-framework/testkit": "^1.22",
+ "alleyinteractive/pest-plugin-wordpress": "^1.0",
"szepeviktor/phpstan-wordpress": "^2.0",
"php-stubs/wp-cli-stubs": "^2.11",
"phpstan/phpstan-deprecation-rules": "^2.0",
"10up/phpcs-composer": "^3.0",
- "phpunit/php-code-coverage": "^9.2",
"slevomat/coding-standard": "^8.15",
"rector/rector": "^2.0",
"tomasvotruba/type-coverage": "^2.0"
},
"scripts": {
- "test": "XDEBUG_MODE=coverage ./vendor/bin/phpunit",
+ "test": "./vendor/bin/pest",
+ "test-coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage",
"lint": "./vendor/bin/phpcs --standard=./phpcs.xml",
"lint-fix": "./vendor/bin/phpcbf --standard=./phpcs.xml",
"static": [
@@ -64,11 +67,13 @@
"generate-class-cache": "@php bin/tenup-framework-generate-class-cache"
},
"scripts-descriptions": {
+ "test-coverage": "Run the test suite with coverage. Requires Xdebug or PCOV; plain `composer test` does not.",
"generate-class-cache": "Generate the class-loader cache for one or more directories, e.g. `composer generate-class-cache -- inc/`."
},
"config": {
"allow-plugins": {
- "dealerdirect/phpcodesniffer-composer-installer": true
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "pestphp/pest-plugin": true
}
}
}
diff --git a/composer.lock b/composer.lock
index 2406e39..84ba08b 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": "2e94fb31328e23fbe3ad4266f9fd54bf",
+ "content-hash": "b9b90d45f646ff1057811322180f4c40",
"packages": [
{
"name": "illuminate/collections",
@@ -801,52 +801,173 @@
"time": "2025-12-09T17:31:17+00:00"
},
{
- "name": "antecedent/patchwork",
- "version": "2.2.3",
+ "name": "alleyinteractive/pest-plugin-wordpress",
+ "version": "v1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/antecedent/patchwork.git",
- "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce"
+ "url": "https://github.com/alleyinteractive/pest-plugin-wordpress.git",
+ "reference": "5fb4c2042fb8affa1640cc008afc6b74c27dbb76"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/antecedent/patchwork/zipball/8b6b235f405af175259c8f56aea5fc23ab9f03ce",
- "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce",
+ "url": "https://api.github.com/repos/alleyinteractive/pest-plugin-wordpress/zipball/5fb4c2042fb8affa1640cc008afc6b74c27dbb76",
+ "reference": "5fb4c2042fb8affa1640cc008afc6b74c27dbb76",
"shasum": ""
},
"require": {
- "php": ">=7.1.0"
+ "mantle-framework/testkit": "^1.16",
+ "pestphp/pest": "^4.0.0",
+ "pestphp/pest-plugin": "^4.0.0",
+ "php": "^8.3"
},
"require-dev": {
- "phpunit/phpunit": ">=4"
+ "mantle-framework/console": "^1.16",
+ "pestphp/pest-dev-tools": "^4.0",
+ "szepeviktor/phpstan-wordpress": "^2.0"
},
"type": "library",
+ "extra": {
+ "mantle": {
+ "aliases": [],
+ "providers": [
+ "Pest\\PestPluginWordPress\\ServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Autoload.php"
+ ],
+ "psr-4": {
+ "Pest\\PestPluginWordPress\\": "src/"
+ }
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
+ "description": "WordPress Pest Integration",
+ "keywords": [
+ "framework",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/alleyinteractive/pest-plugin-wordpress/issues",
+ "source": "https://github.com/alleyinteractive/pest-plugin-wordpress/tree/v1.0.1"
+ },
+ "time": "2025-12-23T19:38:10+00:00"
+ },
+ {
+ "name": "alleyinteractive/wp-concurrent-remote-requests",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/alleyinteractive/wp-concurrent-remote-requests.git",
+ "reference": "630e20c0f968b286b736c5770b1dd26b975eb9f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/alleyinteractive/wp-concurrent-remote-requests/zipball/630e20c0f968b286b736c5770b1dd26b975eb9f3",
+ "reference": "630e20c0f968b286b736c5770b1dd26b975eb9f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "alleyinteractive/alley-coding-standards": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
"authors": [
{
- "name": "Ignas Rudaitis",
- "email": "ignas.rudaitis@gmail.com"
+ "name": "Sean Fisher",
+ "email": "srtfisher@gmail.com"
}
],
- "description": "Method redefinition (monkey-patching) functionality for PHP.",
- "homepage": "https://antecedent.github.io/patchwork/",
+ "description": "Feature plugin for concurrent HTTP remote requests",
+ "homepage": "https://github.com/alleyinteractive/wp-concurrent-remote-requests",
"keywords": [
- "aop",
- "aspect",
- "interception",
- "monkeypatching",
- "redefinition",
- "runkit",
- "testing"
+ "alleyinteractive",
+ "wp-concurrent-remote-requests"
+ ],
+ "support": {
+ "issues": "https://github.com/alleyinteractive/wp-concurrent-remote-requests/issues",
+ "source": "https://github.com/alleyinteractive/wp-concurrent-remote-requests/tree/v1.1.1"
+ },
+ "time": "2025-09-03T19:59:32+00:00"
+ },
+ {
+ "name": "alleyinteractive/wp-filter-side-effects",
+ "version": "v2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/alleyinteractive/wp-filter-side-effects.git",
+ "reference": "3107f336e7079782e4d16a9361b512c7d569a780"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/alleyinteractive/wp-filter-side-effects/zipball/3107f336e7079782e4d16a9361b512c7d569a780",
+ "reference": "3107f336e7079782e4d16a9361b512c7d569a780",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "alleyinteractive/alley-coding-standards": "^1.0.0",
+ "friendsofphp/php-cs-fixer": "^3.8",
+ "mantle-framework/testkit": "^0.5"
+ },
+ "type": "library",
+ "extra": {
+ "wordpress-autoloader": {
+ "autoload": {
+ "Alley\\": "src/alley/"
+ },
+ "autoload-dev": {
+ "Alley\\": "tests/alley/"
+ }
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/alley/wp/filter-side-effects.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "info@alley.com"
+ }
],
+ "description": "Use a WordPress filter like an action.",
"support": {
- "issues": "https://github.com/antecedent/patchwork/issues",
- "source": "https://github.com/antecedent/patchwork/tree/2.2.3"
+ "issues": "https://github.com/alleyinteractive/wp-filter-side-effects/issues",
+ "source": "https://github.com/alleyinteractive/wp-filter-side-effects/tree/v2.0.0"
},
- "time": "2025-09-17T09:00:56+00:00"
+ "time": "2022-12-29T19:26:23+00:00"
},
{
"name": "automattic/vipwpcs",
@@ -903,42 +1024,56 @@
"time": "2026-07-27T14:33:48+00:00"
},
{
- "name": "brain/monkey",
- "version": "2.7.0",
+ "name": "brianium/paratest",
+ "version": "v7.20.0",
"source": {
"type": "git",
- "url": "https://github.com/Brain-WP/BrainMonkey.git",
- "reference": "ea3aeb3d559ba3c0930b3f4d210b665a4c044d83"
+ "url": "https://github.com/paratestphp/paratest.git",
+ "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/ea3aeb3d559ba3c0930b3f4d210b665a4c044d83",
- "reference": "ea3aeb3d559ba3c0930b3f4d210b665a4c044d83",
+ "url": "https://api.github.com/repos/paratestphp/paratest/zipball/81c80677c9ec0ed4ef16b246167f11dec81a6e3d",
+ "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d",
"shasum": ""
},
"require": {
- "antecedent/patchwork": "^2.1.17",
- "mockery/mockery": "~1.3.6 || ~1.4.4 || ~1.5.1 || ^1.6.10",
- "php": ">=5.6.0"
+ "ext-dom": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-simplexml": "*",
+ "fidry/cpu-core-counter": "^1.3.0",
+ "jean85/pretty-package-versions": "^2.1.1",
+ "php": "~8.3.0 || ~8.4.0 || ~8.5.0",
+ "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1",
+ "phpunit/php-file-iterator": "^6.0.1 || ^7",
+ "phpunit/php-timer": "^8 || ^9",
+ "phpunit/phpunit": "^12.5.14 || ^13.0.5",
+ "sebastian/environment": "^8.0.3 || ^9",
+ "symfony/console": "^7.4.7 || ^8.0.7",
+ "symfony/process": "^7.4.5 || ^8.0.5"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
- "phpcompatibility/php-compatibility": "^9.3.0",
- "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.49 || ^9.6.30"
+ "doctrine/coding-standard": "^14.0.0",
+ "ext-pcntl": "*",
+ "ext-pcov": "*",
+ "ext-posix": "*",
+ "phpstan/phpstan": "^2.1.44",
+ "phpstan/phpstan-deprecation-rules": "^2.0.4",
+ "phpstan/phpstan-phpunit": "^2.0.16",
+ "phpstan/phpstan-strict-rules": "^2.0.10",
+ "symfony/filesystem": "^7.4.6 || ^8.0.6"
},
+ "bin": [
+ "bin/paratest",
+ "bin/paratest_for_phpstorm"
+ ],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.x-dev",
- "dev-version/1": "1.x-dev"
- }
- },
"autoload": {
- "files": [
- "inc/api.php"
- ],
"psr-4": {
- "Brain\\Monkey\\": "src/"
+ "ParaTest\\": [
+ "src/"
+ ]
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -947,158 +1082,128 @@
],
"authors": [
{
- "name": "Giuseppe Mazzapica",
- "email": "giuseppe.mazzapica@gmail.com",
- "homepage": "https://gmazzap.me",
+ "name": "Brian Scaturro",
+ "email": "scaturrob@gmail.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Filippo Tessarotto",
+ "email": "zoeslam@gmail.com",
"role": "Developer"
}
],
- "description": "Mocking utility for PHP functions and WordPress plugin API",
+ "description": "Parallel testing for PHP",
+ "homepage": "https://github.com/paratestphp/paratest",
"keywords": [
- "Monkey Patching",
- "interception",
- "mock",
- "mock functions",
- "mockery",
- "patchwork",
- "redefinition",
- "runkit",
- "test",
+ "concurrent",
+ "parallel",
+ "phpunit",
"testing"
],
"support": {
- "issues": "https://github.com/Brain-WP/BrainMonkey/issues",
- "source": "https://github.com/Brain-WP/BrainMonkey"
+ "issues": "https://github.com/paratestphp/paratest/issues",
+ "source": "https://github.com/paratestphp/paratest/tree/v7.20.0"
},
- "time": "2026-02-05T09:22:14+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/Slamdunk",
+ "type": "github"
+ },
+ {
+ "url": "https://paypal.me/filippotessarotto",
+ "type": "paypal"
+ }
+ ],
+ "time": "2026-03-29T15:46:14+00:00"
},
{
- "name": "dealerdirect/phpcodesniffer-composer-installer",
- "version": "v1.2.1",
+ "name": "brick/math",
+ "version": "0.18.0",
"source": {
"type": "git",
- "url": "https://github.com/PHPCSStandards/composer-installer.git",
- "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd"
+ "url": "https://github.com/brick/math.git",
+ "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
- "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "url": "https://api.github.com/repos/brick/math/zipball/82944324d1c1bdb2c2618e89978d4e2ad78d69ad",
+ "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad",
"shasum": ""
},
"require": {
- "composer-plugin-api": "^2.2",
- "php": ">=5.4",
- "squizlabs/php_codesniffer": "^3.1.0 || ^4.0"
+ "php": "^8.2"
},
"require-dev": {
- "composer/composer": "^2.2",
- "ext-json": "*",
- "ext-zip": "*",
- "php-parallel-lint/php-parallel-lint": "^1.4.0",
- "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev",
- "yoast/phpunit-polyfills": "^1.0"
- },
- "type": "composer-plugin",
- "extra": {
- "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ "phpstan/phpstan": "2.1.22",
+ "phpunit/phpunit": "^11.5"
},
+ "type": "library",
"autoload": {
"psr-4": {
- "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ "Brick\\Math\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "authors": [
- {
- "name": "Franck Nijhof",
- "email": "opensource@frenck.dev",
- "homepage": "https://frenck.dev",
- "role": "Open source developer"
- },
- {
- "name": "Contributors",
- "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
- }
- ],
- "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "description": "Arbitrary-precision arithmetic library",
"keywords": [
- "PHPCodeSniffer",
- "PHP_CodeSniffer",
- "code quality",
- "codesniffer",
- "composer",
- "installer",
- "phpcbf",
- "phpcs",
- "plugin",
- "qa",
- "quality",
- "standard",
- "standards",
- "style guide",
- "stylecheck",
- "tests"
+ "Arbitrary-precision",
+ "BigInteger",
+ "BigRational",
+ "arithmetic",
+ "bigdecimal",
+ "bignum",
+ "bignumber",
+ "brick",
+ "decimal",
+ "integer",
+ "math",
+ "mathematics",
+ "rational"
],
"support": {
- "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
- "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
- "source": "https://github.com/PHPCSStandards/composer-installer"
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.18.0"
},
"funding": [
{
- "url": "https://github.com/PHPCSStandards",
- "type": "github"
- },
- {
- "url": "https://github.com/jrfnl",
+ "url": "https://github.com/BenMorel",
"type": "github"
- },
- {
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
- },
- {
- "url": "https://thanks.dev/u/gh/phpcsstandards",
- "type": "thanks_dev"
}
],
- "time": "2026-05-06T08:26:05+00:00"
+ "time": "2026-06-14T18:21:03+00:00"
},
{
- "name": "doctrine/instantiator",
- "version": "2.0.0",
+ "name": "carbonphp/carbon-doctrine-types",
+ "version": "3.2.0",
"source": {
"type": "git",
- "url": "https://github.com/doctrine/instantiator.git",
- "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
+ "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
- "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
"shasum": ""
},
"require": {
"php": "^8.1"
},
+ "conflict": {
+ "doctrine/dbal": "<4.0.0 || >=5.0.0"
+ },
"require-dev": {
- "doctrine/coding-standard": "^11",
- "ext-pdo": "*",
- "ext-phar": "*",
- "phpbench/phpbench": "^1.2",
- "phpstan/phpstan": "^1.9.4",
- "phpstan/phpstan-phpunit": "^1.3",
- "phpunit/phpunit": "^9.5.27",
- "vimeo/psalm": "^5.4"
+ "doctrine/dbal": "^4.0.0",
+ "nesbot/carbon": "^2.71.0 || ^3.0.0",
+ "phpunit/phpunit": "^10.3"
},
"type": "library",
"autoload": {
"psr-4": {
- "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1107,867 +1212,6471 @@
],
"authors": [
{
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com",
- "homepage": "https://ocramius.github.io/"
+ "name": "KyleKatarn",
+ "email": "kylekatarnls@gmail.com"
}
],
- "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
- "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "description": "Types to use Carbon in Doctrine",
"keywords": [
- "constructor",
- "instantiate"
+ "carbon",
+ "date",
+ "datetime",
+ "doctrine",
+ "time"
],
"support": {
- "issues": "https://github.com/doctrine/instantiator/issues",
- "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
+ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+ "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
},
"funding": [
{
- "url": "https://www.doctrine-project.org/sponsorship.html",
- "type": "custom"
+ "url": "https://github.com/kylekatarnls",
+ "type": "github"
},
{
- "url": "https://www.patreon.com/phpdoctrine",
- "type": "patreon"
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
},
{
- "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
"type": "tidelift"
}
],
- "time": "2022-12-30T00:23:10+00:00"
+ "time": "2024-02-09T16:56:22+00:00"
},
{
- "name": "hamcrest/hamcrest-php",
- "version": "v2.1.1",
+ "name": "composer/pcre",
+ "version": "3.4.0",
"source": {
"type": "git",
- "url": "https://github.com/hamcrest/hamcrest-php.git",
- "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "d5a341b3fb61f3001970940afb1d332968a183ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
- "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed",
+ "reference": "d5a341b3fb61f3001970940afb1d332968a183ed",
"shasum": ""
},
"require": {
- "php": "^7.4|^8.0"
+ "php": "^7.4 || ^8.0"
},
- "replace": {
- "cordoval/hamcrest-php": "*",
- "davedevelopment/hamcrest-php": "*",
- "kodova/hamcrest-php": "*"
+ "conflict": {
+ "phpstan/phpstan": "<2.2.2"
},
"require-dev": {
- "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
- "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpunit/phpunit": "^9"
},
"type": "library",
"extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
"branch-alias": {
- "dev-master": "2.1-dev"
+ "dev-main": "3.x-dev"
}
},
"autoload": {
- "classmap": [
- "hamcrest"
- ]
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
],
- "description": "This is the PHP port of Hamcrest Matchers",
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"keywords": [
- "test"
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
],
"support": {
- "issues": "https://github.com/hamcrest/hamcrest-php/issues",
- "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.4.0"
},
- "time": "2025-04-30T06:54:44+00:00"
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ }
+ ],
+ "time": "2026-06-07T11:47:49+00:00"
},
{
- "name": "mockery/mockery",
- "version": "1.6.12",
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
"source": {
"type": "git",
- "url": "https://github.com/mockery/mockery.git",
- "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
- "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
"shasum": ""
},
"require": {
- "hamcrest/hamcrest-php": "^2.0.1",
- "lib-pcre": ">=7.0",
- "php": ">=7.3"
- },
- "conflict": {
- "phpunit/phpunit": "<8.0"
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
},
"require-dev": {
- "phpunit/phpunit": "^8.5 || ^9.6.17",
- "symplify/easy-coding-standard": "^12.1.14"
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
},
"type": "library",
"autoload": {
- "files": [
- "library/helpers.php",
- "library/Mockery.php"
- ],
"psr-4": {
- "Mockery\\": "library/Mockery"
+ "Composer\\XdebugHandler\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Pádraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "https://github.com/padraic",
- "role": "Author"
- },
- {
- "name": "Dave Marshall",
- "email": "dave.marshall@atstsolutions.co.uk",
- "homepage": "https://davedevelopment.co.uk",
- "role": "Developer"
- },
- {
- "name": "Nathanael Esayeas",
- "email": "nathanael.esayeas@protonmail.com",
- "homepage": "https://github.com/ghostwriter",
- "role": "Lead Developer"
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
}
],
- "description": "Mockery is a simple yet flexible PHP mock object framework",
- "homepage": "https://github.com/mockery/mockery",
+ "description": "Restarts a process without Xdebug.",
"keywords": [
- "BDD",
- "TDD",
- "library",
- "mock",
- "mock objects",
- "mockery",
- "stub",
- "test",
- "test double",
- "testing"
+ "Xdebug",
+ "performance"
],
"support": {
- "docs": "https://docs.mockery.io/",
- "issues": "https://github.com/mockery/mockery/issues",
- "rss": "https://github.com/mockery/mockery/releases.atom",
- "security": "https://github.com/mockery/mockery/security/advisories",
- "source": "https://github.com/mockery/mockery"
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
},
- "time": "2024-05-16T03:13:13+00:00"
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-06T16:37:16+00:00"
},
{
- "name": "myclabs/deep-copy",
- "version": "1.13.4",
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.2.1",
"source": {
"type": "git",
- "url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
- "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
"shasum": ""
},
"require": {
- "php": "^7.1 || ^8.0"
- },
- "conflict": {
- "doctrine/collections": "<1.6.8",
- "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.1.0 || ^4.0"
},
"require-dev": {
- "doctrine/collections": "^1.6.8",
- "doctrine/common": "^2.13.3 || ^3.2.2",
- "phpspec/prophecy": "^1.10",
- "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ "composer/composer": "^2.2",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
- "type": "library",
"autoload": {
- "files": [
- "src/DeepCopy/deep_copy.php"
- ],
"psr-4": {
- "DeepCopy\\": "src/DeepCopy/"
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "Create deep copies (clones) of your objects",
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "opensource@frenck.dev",
+ "homepage": "https://frenck.dev",
+ "role": "Open source developer"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"keywords": [
- "clone",
- "copy",
- "duplicate",
- "object",
- "object graph"
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
],
"support": {
- "issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
},
"funding": [
{
- "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
- "type": "tidelift"
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
}
],
- "time": "2025-08-01T08:46:24+00:00"
+ "time": "2026-05-06T08:26:05+00:00"
},
{
- "name": "nette/utils",
- "version": "v4.1.5",
+ "name": "dflydev/dot-access-data",
+ "version": "v3.0.3",
"source": {
"type": "git",
- "url": "https://github.com/nette/utils.git",
- "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0"
+ "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/b043439dbdf954e6c28b5ea7e34b0100f83165e0",
- "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0",
+ "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
"shasum": ""
},
"require": {
- "php": "8.2 - 8.5"
- },
- "conflict": {
- "nette/finder": "<3",
- "nette/schema": "<1.2.2"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
- "jetbrains/phpstorm-attributes": "^1.2",
- "nette/phpstan-rules": "^1.0",
- "nette/tester": "^2.5",
- "phpstan/extension-installer": "^1.4@stable",
- "phpstan/phpstan": "^2.1@stable",
- "tracy/tracy": "^2.9"
- },
- "suggest": {
- "ext-gd": "to use Image",
- "ext-iconv": "to use Strings::chr(), ord() and reverse()",
- "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
- "ext-json": "to use Nette\\Utils\\Json",
- "ext-mbstring": "to use Strings::lower() etc...",
- "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.1-dev"
+ "dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
- "Nette\\": "src"
- },
- "classmap": [
- "src/"
- ]
+ "Dflydev\\DotAccessData\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause",
- "GPL-2.0-only",
- "GPL-3.0-only"
+ "MIT"
],
"authors": [
{
- "name": "David Grudl",
- "homepage": "https://davidgrudl.com"
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com"
},
{
- "name": "Nette Community",
- "homepage": "https://nette.org/contributors"
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com"
}
],
- "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
- "homepage": "https://nette.org",
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
"keywords": [
- "array",
- "core",
- "datetime",
- "images",
- "json",
- "nette",
- "paginator",
- "password",
- "slugify",
- "string",
- "unicode",
- "utf-8",
- "utility",
- "validation"
+ "access",
+ "data",
+ "dot",
+ "notation"
],
"support": {
- "issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.1.5"
+ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+ "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
},
- "time": "2026-07-17T23:02:45+00:00"
+ "time": "2024-07-08T12:26:09+00:00"
},
{
- "name": "nikic/php-parser",
- "version": "v5.8.0",
+ "name": "doctrine/deprecations",
+ "version": "1.1.6",
"source": {
"type": "git",
- "url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f"
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
- "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
"shasum": ""
},
"require": {
- "ext-json": "*",
- "ext-tokenizer": "*",
- "php": ">=7.4"
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<=7.5 || >=14"
},
"require-dev": {
- "ircmaxell/php-yacc": "^0.0.7",
- "phpunit/phpunit": "^9.0"
+ "doctrine/coding-standard": "^9 || ^12 || ^14",
+ "phpstan/phpstan": "1.4.10 || 2.1.30",
+ "phpstan/phpstan-phpunit": "^1.0 || ^2",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
+ "psr/log": "^1 || ^2 || ^3"
},
- "bin": [
- "bin/php-parse"
- ],
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.x-dev"
- }
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
+ "type": "library",
"autoload": {
"psr-4": {
- "PhpParser\\": "lib/PhpParser"
+ "Doctrine\\Deprecations\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Nikita Popov"
- }
- ],
- "description": "A PHP parser written in PHP",
- "keywords": [
- "parser",
- "php"
+ "MIT"
],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
"support": {
- "issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0"
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.6"
},
- "time": "2026-07-04T14:30:18+00:00"
+ "time": "2026-02-07T07:09:04+00:00"
},
{
- "name": "phar-io/manifest",
- "version": "2.0.4",
+ "name": "doctrine/inflector",
+ "version": "2.1.0",
"source": {
"type": "git",
- "url": "https://github.com/phar-io/manifest.git",
- "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
- "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b",
+ "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-libxml": "*",
- "ext-phar": "*",
- "ext-xmlwriter": "*",
- "phar-io/version": "^3.0.1",
"php": "^7.2 || ^8.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0 || ^13.0",
+ "phpstan/phpstan": "^1.12 || ^2.0",
+ "phpstan/phpstan-phpunit": "^1.4 || ^2.0",
+ "phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
+ "phpunit/phpunit": "^8.5 || ^12.2"
},
+ "type": "library",
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Doctrine\\Inflector\\": "src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
},
{
- "name": "Sebastian Heuer",
- "email": "sebastian@phpeople.de",
- "role": "Developer"
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
},
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "Developer"
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
}
],
- "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
"support": {
- "issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.1.0"
},
"funding": [
{
- "url": "https://github.com/theseer",
- "type": "github"
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
}
],
- "time": "2024-03-03T12:33:53+00:00"
+ "time": "2025-08-10T19:31:58+00:00"
},
{
- "name": "phar-io/version",
- "version": "3.2.1",
+ "name": "fakerphp/faker",
+ "version": "v1.24.1",
"source": {
"type": "git",
- "url": "https://github.com/phar-io/version.git",
- "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
- "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0"
+ "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": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Faker\\": "src/Faker/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Heuer",
- "email": "sebastian@phpeople.de",
- "role": "Developer"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "Developer"
+ "name": "François Zaninotto"
}
],
- "description": "Library for handling version information and constraints",
+ "description": "Faker is a PHP library that generates fake data for you.",
+ "keywords": [
+ "data",
+ "faker",
+ "fixtures"
+ ],
"support": {
- "issues": "https://github.com/phar-io/version/issues",
- "source": "https://github.com/phar-io/version/tree/3.2.1"
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1"
},
- "time": "2022-02-21T01:04:05+00:00"
+ "time": "2024-11-21T13:46:39+00:00"
},
{
- "name": "php-stubs/wordpress-stubs",
- "version": "v6.9.4",
+ "name": "fidry/cpu-core-counter",
+ "version": "1.3.0",
"source": {
"type": "git",
- "url": "https://github.com/php-stubs/wordpress-stubs.git",
- "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5"
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5",
- "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678",
"shasum": ""
},
- "conflict": {
- "phpdocumentor/reflection-docblock": "5.6.1"
+ "require": {
+ "php": "^7.2 || ^8.0"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
- "nikic/php-parser": "^5.5",
- "php": "^7.4 || ^8.0",
- "php-stubs/generator": "^0.8.6",
- "phpdocumentor/reflection-docblock": "^6.0",
- "phpstan/phpstan": "^2.1",
- "phpunit/phpunit": "^9.5",
- "symfony/polyfill-php80": "*",
- "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1",
- "wp-coding-standards/wpcs": "3.1.0 as 2.3.0"
- },
- "suggest": {
- "paragonie/sodium_compat": "Pure PHP implementation of libsodium",
- "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
- "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-deprecation-rules": "^2.0.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
},
"type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "WordPress function and class declaration stubs for static analysis.",
- "homepage": "https://github.com/php-stubs/wordpress-stubs",
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
"keywords": [
- "PHPStan",
- "static analysis",
- "wordpress"
+ "CPU",
+ "core"
],
"support": {
- "issues": "https://github.com/php-stubs/wordpress-stubs/issues",
- "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4"
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0"
},
- "time": "2026-05-01T20:36:01+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-14T07:29:31+00:00"
},
{
- "name": "php-stubs/wp-cli-stubs",
- "version": "v2.12.0",
+ "name": "filp/whoops",
+ "version": "2.18.4",
"source": {
"type": "git",
- "url": "https://github.com/php-stubs/wp-cli-stubs.git",
- "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d"
+ "url": "https://github.com/filp/whoops.git",
+ "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/af16401e299a3fd2229bd0fa9a037638a4174a9d",
- "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d",
+ "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d",
"shasum": ""
},
"require": {
- "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0"
+ "php": "^7.1 || ^8.0",
+ "psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
- "php": "~7.3 || ~8.0",
- "php-stubs/generator": "^0.8.0"
+ "mockery/mockery": "^1.0",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3",
+ "symfony/var-dumper": "^4.0 || ^5.0"
},
"suggest": {
- "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
- "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "WP-CLI function and class declaration stubs for static analysis.",
- "homepage": "https://github.com/php-stubs/wp-cli-stubs",
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "description": "php error handling for cool kids",
+ "homepage": "https://filp.github.io/whoops/",
"keywords": [
- "PHPStan",
- "static analysis",
- "wordpress",
- "wp-cli"
+ "error",
+ "exception",
+ "handling",
+ "library",
+ "throwable",
+ "whoops"
],
"support": {
- "issues": "https://github.com/php-stubs/wp-cli-stubs/issues",
- "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.12.0"
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.18.4"
},
- "time": "2025-06-10T09:58:05+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-08T12:00:00+00:00"
},
{
- "name": "phpcompatibility/php-compatibility",
- "version": "9.3.5",
+ "name": "graham-campbell/result-type",
+ "version": "v1.1.4",
"source": {
"type": "git",
- "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
+ "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
"shasum": ""
},
"require": {
- "php": ">=5.3",
- "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
- },
- "conflict": {
- "squizlabs/php_codesniffer": "2.6.2"
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.5"
},
"require-dev": {
- "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
+ "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
},
- "suggest": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
},
- "type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
- "LGPL-3.0-or-later"
+ "MIT"
],
"authors": [
{
- "name": "Wim Godden",
- "homepage": "https://github.com/wimg",
- "role": "lead"
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
},
{
- "name": "Juliette Reinders Folmer",
- "homepage": "https://github.com/jrfnl",
- "role": "lead"
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-27T19:43:20+00:00"
+ },
+ {
+ "name": "jean85/pretty-package-versions",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Jean85/pretty-package-versions.git",
+ "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
+ "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.1.0",
+ "php": "^7.4|^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpunit/phpunit": "^7.5|^8.5|^9.6",
+ "rector/rector": "^2.0",
+ "vimeo/psalm": "^4.3 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Jean85\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Alessandro Lai",
+ "email": "alessandro.lai85@gmail.com"
+ }
+ ],
+ "description": "A library to get pretty versions strings of installed dependencies",
+ "keywords": [
+ "composer",
+ "package",
+ "release",
+ "versions"
+ ],
+ "support": {
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues",
+ "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
+ },
+ "time": "2025-03-19T14:43:43+00:00"
+ },
+ {
+ "name": "laravel/serializable-closure",
+ "version": "v2.0.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/serializable-closure.git",
+ "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661",
+ "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "illuminate/support": "^10.0|^11.0|^12.0|^13.0",
+ "nesbot/carbon": "^2.67|^3.0",
+ "pestphp/pest": "^2.36|^3.0|^4.0",
+ "phpstan/phpstan": "^2.0",
+ "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\SerializableClosure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
},
{
- "name": "Contributors",
- "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
+ "name": "Nuno Maduro",
+ "email": "nuno@laravel.com"
}
],
- "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
- "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
+ "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
"keywords": [
- "compatibility",
- "phpcs",
- "standards"
+ "closure",
+ "laravel",
+ "serializable"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/serializable-closure/issues",
+ "source": "https://github.com/laravel/serializable-closure"
+ },
+ "time": "2026-07-21T16:49:22+00:00"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "2.8.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/1902f60f984235023acbe03db6ad614a37b3c3e7",
+ "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "php": "^7.4 || ^8.0",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^2.0.0",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0 || ^12.0.0 || ^13.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
+ },
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-12T15:29:16+00:00"
+ },
+ {
+ "name": "league/config",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/config.git",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "shasum": ""
+ },
+ "require": {
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "homepage": "https://config.thephpleague.com",
+ "keywords": [
+ "array",
+ "config",
+ "configuration",
+ "dot",
+ "dot-access",
+ "nested",
+ "schema"
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-11T20:36:23+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "3.35.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "b277b5dc3d56650b68904117124e79c851e12376"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376",
+ "reference": "b277b5dc3d56650b68904117124e79c851e12376",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem-local": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "conflict": {
+ "async-aws/core": "<1.19.0",
+ "async-aws/s3": "<1.14.0",
+ "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+ "guzzlehttp/guzzle": "<7.0",
+ "guzzlehttp/ringphp": "<1.1.1",
+ "phpseclib/phpseclib": "3.0.15",
+ "symfony/http-client": "<5.2"
+ },
+ "require-dev": {
+ "async-aws/s3": "^1.5 || ^2.0",
+ "async-aws/simple-s3": "^1.1 || ^2.0",
+ "aws/aws-sdk-php": "^3.295.10",
+ "composer/semver": "^3.0",
+ "ext-fileinfo": "*",
+ "ext-ftp": "*",
+ "ext-mongodb": "^1.3|^2",
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.5",
+ "google/cloud-storage": "^1.23",
+ "guzzlehttp/psr7": "^2.6",
+ "microsoft/azure-storage-blob": "^1.1",
+ "mongodb/mongodb": "^1.2|^2",
+ "phpseclib/phpseclib": "^3.0.36",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^9.5.11|^10.0",
+ "sabre/dav": "^4.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "File storage abstraction for PHP",
+ "keywords": [
+ "WebDAV",
+ "aws",
+ "cloud",
+ "file",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/3.35.2"
+ },
+ "time": "2026-07-06T14:42:07+00:00"
+ },
+ {
+ "name": "league/flysystem-local",
+ "version": "3.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-local.git",
+ "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+ "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/flysystem": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Local\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Local filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "local"
+ ],
+ "support": {
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0"
+ },
+ "time": "2026-01-23T15:30:45+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76",
+ "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-09T11:49:27+00:00"
+ },
+ {
+ "name": "league/uri",
+ "version": "7.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri.git",
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4",
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4",
+ "shasum": ""
+ },
+ "require": {
+ "league/uri-interfaces": "^7.8.1",
+ "php": "^8.1",
+ "psr/http-factory": "^1"
+ },
+ "conflict": {
+ "league/uri-schemes": "^1.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-dom": "to convert the URI into an HTML anchor tag",
+ "ext-fileinfo": "to create Data URI from file contennts",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "ext-uri": "to use the PHP native URI class",
+ "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
+ "league/uri-components": "to provide additional tools to manipulate URI objects components",
+ "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
+ "php-64bit": "to improve IPV4 host parsing",
+ "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "URI manipulation library",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "URN",
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "middleware",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc2141",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "rfc8141",
+ "uri",
+ "uri-template",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri/tree/7.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2026-03-15T20:22:25+00:00"
+ },
+ {
+ "name": "league/uri-interfaces",
+ "version": "7.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri-interfaces.git",
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928",
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^8.1",
+ "psr/http-message": "^1.1 || ^2.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "php-64bit": "to improve IPV4 host parsing",
+ "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2026-03-08T20:05:35+00:00"
+ },
+ {
+ "name": "mantle-framework/cache",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/cache.git",
+ "reference": "5197264c9eec22e899d2bd3f0f9141353aad057b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/cache/zipball/5197264c9eec22e899d2bd3f0f9141353aad057b",
+ "reference": "5197264c9eec22e899d2bd3f0f9141353aad057b",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "nesbot/carbon": "^3.8.4",
+ "php": "^8.3"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Cache\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Cache Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/cache/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:25+00:00"
+ },
+ {
+ "name": "mantle-framework/config",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/config.git",
+ "reference": "16cc74d63c26c5a36471accacc33b01244fde134"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/config/zipball/16cc74d63c26c5a36471accacc33b01244fde134",
+ "reference": "16cc74d63c26c5a36471accacc33b01244fde134",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Config\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Config Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/config/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:23+00:00"
+ },
+ {
+ "name": "mantle-framework/container",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/container.git",
+ "reference": "f01617ac68af2b830b4b6a767a0c563340ef16ce"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/container/zipball/f01617ac68af2b830b4b6a767a0c563340ef16ce",
+ "reference": "f01617ac68af2b830b4b6a767a0c563340ef16ce",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "php": "^8.3",
+ "psr/container": "^1.1.1 || ^2.0.2"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Container\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Container Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/container/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:24+00:00"
+ },
+ {
+ "name": "mantle-framework/contracts",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/contracts.git",
+ "reference": "41b07278128835033399c6ea98a1cf22b5a7638d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/contracts/zipball/41b07278128835033399c6ea98a1cf22b5a7638d",
+ "reference": "41b07278128835033399c6ea98a1cf22b5a7638d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.3",
+ "psr/container": "^1.1.1 || ^2.0.2"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Contracts\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Contracts Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/contracts/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:31+00:00"
+ },
+ {
+ "name": "mantle-framework/database",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/database.git",
+ "reference": "fab51c0bb977a75ed5130c89a21f4d35537297bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/database/zipball/fab51c0bb977a75ed5130c89a21f4d35537297bd",
+ "reference": "fab51c0bb977a75ed5130c89a21f4d35537297bd",
+ "shasum": ""
+ },
+ "require": {
+ "alleyinteractive/wp-filter-side-effects": "^1.0 || ^2.0",
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3",
+ "psr/container": "^1.1.1 || ^2.0.2",
+ "symfony/finder": "^7.2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Database\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Database Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/database/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:27+00:00"
+ },
+ {
+ "name": "mantle-framework/events",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/events.git",
+ "reference": "b516e66c4e98c38ce44eb325371d6a63be868968"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/events/zipball/b516e66c4e98c38ce44eb325371d6a63be868968",
+ "reference": "b516e66c4e98c38ce44eb325371d6a63be868968",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/container": "^1.0",
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3",
+ "psr/container": "^1.1.1 || ^2.0.2",
+ "symfony/finder": "^7.2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Events\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Events Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/events/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:25+00:00"
+ },
+ {
+ "name": "mantle-framework/faker",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/faker.git",
+ "reference": "a56893818fb430e30223869cef2a80c5232880e4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/faker/zipball/a56893818fb430e30223869cef2a80c5232880e4",
+ "reference": "a56893818fb430e30223869cef2a80c5232880e4",
+ "shasum": ""
+ },
+ "require": {
+ "fakerphp/faker": "^1.24",
+ "php": "^8.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Faker\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Faker Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/faker/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:24+00:00"
+ },
+ {
+ "name": "mantle-framework/filesystem",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/filesystem.git",
+ "reference": "1f5b82829470158a64bf29e2c5c9052adb31aa0e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/filesystem/zipball/1f5b82829470158a64bf29e2c5c9052adb31aa0e",
+ "reference": "1f5b82829470158a64bf29e2c5c9052adb31aa0e",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem": "^3.29",
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3",
+ "psr/container": "^1.1.1 || ^2.0.2",
+ "symfony/finder": "^7.2.0",
+ "symfony/http-foundation": "^7.3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\Filesystem\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Filesystem Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/filesystem/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:26+00:00"
+ },
+ {
+ "name": "mantle-framework/framework-views",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/framework-views.git",
+ "reference": "c66a546752b02d08add1f211267575a29732da93"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/framework-views/zipball/c66a546752b02d08add1f211267575a29732da93",
+ "reference": "c66a546752b02d08add1f211267575a29732da93",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/view": "^1.0",
+ "php": "^8.3"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework package for framework views.",
+ "support": {
+ "source": "https://github.com/mantle-framework/framework-views/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:30+00:00"
+ },
+ {
+ "name": "mantle-framework/http",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/http.git",
+ "reference": "1fcbe17871e7796105a98746adcd53a33f238ba6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/http/zipball/1fcbe17871e7796105a98746adcd53a33f238ba6",
+ "reference": "1fcbe17871e7796105a98746adcd53a33f238ba6",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/filesystem": "^1.0",
+ "mantle-framework/framework-views": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3",
+ "symfony/http-foundation": "^7.3.7",
+ "symfony/http-kernel": "^7.2.0",
+ "symfony/mime": "^7.2.0",
+ "symfony/routing": "^7.2.0",
+ "symfony/var-dumper": "^7.2.0"
+ },
+ "suggest": {
+ "illuminate/view": "For assertions in tests that use Blade templating"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Http\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Http Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/http/tree/v1.22.0"
+ },
+ "time": "2026-07-03T03:08:50+00:00"
+ },
+ {
+ "name": "mantle-framework/http-client",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/http-client.git",
+ "reference": "aeda5d6b7c0f0cee346f950ee34c5412a57ff707"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/http-client/zipball/aeda5d6b7c0f0cee346f950ee34c5412a57ff707",
+ "reference": "aeda5d6b7c0f0cee346f950ee34c5412a57ff707",
+ "shasum": ""
+ },
+ "require": {
+ "alleyinteractive/wp-concurrent-remote-requests": "^1.0.2",
+ "mantle-framework/cache": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "php": "^8.3"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Http_Client\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Http Client Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/http-client/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:22+00:00"
+ },
+ {
+ "name": "mantle-framework/support",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/support.git",
+ "reference": "7880f9583513f317b20d0070b012f733a9a9b68e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/support/zipball/7880f9583513f317b20d0070b012f733a9a9b68e",
+ "reference": "7880f9583513f317b20d0070b012f733a9a9b68e",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/inflector": "^2.0.8",
+ "laravel/serializable-closure": "^1.3.1 || ^2.0",
+ "league/commonmark": "^2.8.2",
+ "league/uri": "^7.5",
+ "league/uri-interfaces": "^7.5",
+ "mantle-framework/contracts": "^1.0",
+ "monolog/monolog": "^2.9.1",
+ "nesbot/carbon": "^3.8.4",
+ "php": "^8.3",
+ "ramsey/uuid": "^4.7.4",
+ "spatie/backtrace": "^1.8",
+ "symfony/dom-crawler": "^7.4.12",
+ "symfony/finder": "^7.2.0",
+ "symfony/var-dumper": "^7.2.0",
+ "vlucas/phpdotenv": "^5.6.1",
+ "voku/portable-ascii": "^2.0.1"
+ },
+ "conflict": {
+ "masterminds/html5": "<2.9"
+ },
+ "suggest": {
+ "mantle-framework/console": "Required to load Mantle Console Commands",
+ "mantle-framework/container": "Required to use wrapped add_action/add_filter functions",
+ "vlucas/phpdotenv": "Required to use Environment class (^5.3)"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Support\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Support Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/support/tree/v1.22.0"
+ },
+ "time": "2026-07-03T03:08:52+00:00"
+ },
+ {
+ "name": "mantle-framework/testing",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/testing.git",
+ "reference": "b1b12c568d2d7eb9c241ccda56ac1281e2496d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/testing/zipball/b1b12c568d2d7eb9c241ccda56ac1281e2496d5b",
+ "reference": "b1b12c568d2d7eb9c241ccda56ac1281e2496d5b",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/database": "^1.0",
+ "mantle-framework/faker": "^1.0",
+ "mantle-framework/http": "^1.0",
+ "mantle-framework/http-client": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "mantle-framework/testing-dependencies": "^1.0",
+ "php": "^8.3"
+ },
+ "suggest": {
+ "brianium/paratest": "For running tests in parallel.",
+ "mantle-framework/console": "Required to assert console commands.",
+ "mantle-framework/testkit": "For running tests against a WordPress install without Mantle",
+ "nunomaduro/collision": "For better PHPUnit printing.",
+ "phpunit/phpunit": "Required to use assertions and run tests."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Testing\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Testing Package",
+ "keywords": [
+ "mantle",
+ "testing"
+ ],
+ "support": {
+ "source": "https://github.com/mantle-framework/testing/tree/v1.22.0"
+ },
+ "time": "2026-07-03T03:08:49+00:00"
+ },
+ {
+ "name": "mantle-framework/testing-dependencies",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/testing-dependencies.git",
+ "reference": "55fff097fcff17a90fb5ad2ca7f3ff06a0dc0170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/testing-dependencies/zipball/55fff097fcff17a90fb5ad2ca7f3ff06a0dc0170",
+ "reference": "55fff097fcff17a90fb5ad2ca7f3ff06a0dc0170",
+ "shasum": ""
+ },
+ "require": {
+ "fakerphp/faker": "^1.24",
+ "filp/whoops": "^2.18.1",
+ "myclabs/deep-copy": "^1.13",
+ "nunomaduro/termwind": "^1.15.1 || ^2.0",
+ "php": "^8.3",
+ "phpunit/phpunit": "^10.5.62 || ^11.5.50 || ^12.5.8 || ^13.1",
+ "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1",
+ "symfony/css-selector": "^7.2.0",
+ "symfony/http-foundation": "^7.3.7"
+ },
+ "suggest": {
+ "brianium/paratest": "For running tests in parallel.",
+ "mantle-framework/testkit": "For running tests against a WordPress install without Mantle",
+ "nunomaduro/collision": "For better PHPUnit printing."
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Testing Dependencies Package",
+ "keywords": [
+ "mantle",
+ "testing"
+ ],
+ "support": {
+ "source": "https://github.com/mantle-framework/testing-dependencies/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:23+00:00"
+ },
+ {
+ "name": "mantle-framework/testkit",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/testkit.git",
+ "reference": "d01653bbec79cf77c7c26a7c5ae8d923c055ecd0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/testkit/zipball/d01653bbec79cf77c7c26a7c5ae8d923c055ecd0",
+ "reference": "d01653bbec79cf77c7c26a7c5ae8d923c055ecd0",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/config": "^1.0",
+ "mantle-framework/container": "^1.0",
+ "mantle-framework/contracts": "^1.0",
+ "mantle-framework/events": "^1.0",
+ "mantle-framework/faker": "^1.0",
+ "mantle-framework/support": "^1.0",
+ "mantle-framework/testing": "^1.0",
+ "mantle-framework/testing-dependencies": "^1.0",
+ "nunomaduro/collision": "^6.0 || ^7.0 || ^8.0",
+ "php": "^8.3",
+ "phpunit/phpunit": "^10.5.62 || ^11.5.50 || ^12.5.8 || ^13.1",
+ "symfony/http-foundation": "^7.3.7"
+ },
+ "conflict": {
+ "alleyinteractive/mantle-framework": "*"
+ },
+ "suggest": {
+ "alleyinteractive/mantle-framework": "For the full Mantle Framework, require the base package instead."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "Mantle\\Testkit\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework Testkit Package",
+ "keywords": [
+ "mantle",
+ "testing"
+ ],
+ "support": {
+ "source": "https://github.com/mantle-framework/testkit/tree/v1.22.0"
+ },
+ "time": "2026-06-24T17:27:24+00:00"
+ },
+ {
+ "name": "mantle-framework/view",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mantle-framework/view.git",
+ "reference": "5d0943801a92323b8a349825383991dc348a5c7b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mantle-framework/view/zipball/5d0943801a92323b8a349825383991dc348a5c7b",
+ "reference": "5d0943801a92323b8a349825383991dc348a5c7b",
+ "shasum": ""
+ },
+ "require": {
+ "mantle-framework/contracts": "^1.0",
+ "php": "^8.3"
+ },
+ "suggest": {
+ "phpunit/phpunit": "Required to use assertions and run tests."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Mantle\\View\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Alley",
+ "email": "mantle@alley.com"
+ }
+ ],
+ "description": "The Mantle Framework View Package",
+ "support": {
+ "source": "https://github.com/mantle-framework/view/tree/v1.22.0"
+ },
+ "time": "2026-07-03T03:08:50+00:00"
+ },
+ {
+ "name": "masterminds/html5",
+ "version": "2.10.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "fd5018f6815fff903946d0564977b44ce8010e29"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fd5018f6815fff903946d0564977b44ce8010e29",
+ "reference": "fd5018f6815fff903946d0564977b44ce8010e29",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9 || ^10"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.10.1"
+ },
+ "time": "2026-06-23T18:43:15+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "2.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "37308608e599f34a1a4845b16440047ec98a172a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/37308608e599f34a1a4845b16440047ec98a172a",
+ "reference": "37308608e599f34a1a4845b16440047ec98a172a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2",
+ "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7 || ^8",
+ "ext-json": "*",
+ "graylog2/gelf-php": "^1.4.2 || ^2@dev",
+ "guzzlehttp/guzzle": "^7.4",
+ "guzzlehttp/psr7": "^2.2",
+ "mongodb/mongodb": "^1.8 || ^2.0",
+ "php-amqplib/php-amqplib": "~2.4 || ^3",
+ "phpspec/prophecy": "^1.15",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^8.5.38 || ^9.6.19",
+ "predis/predis": "^1.1 || ^2.0",
+ "rollbar/rollbar": "^1.3 || ^2 || ^3",
+ "ruflin/elastica": "^7",
+ "swiftmailer/swiftmailer": "^5.3|^6.0",
+ "symfony/mailer": "^5.4 || ^6",
+ "symfony/mime": "^5.4 || ^6"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/2.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-01T13:05:00+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "3.13.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon.git",
+ "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2",
+ "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2",
+ "shasum": ""
+ },
+ "require": {
+ "carbonphp/carbon-doctrine-types": "<100.0",
+ "ext-json": "*",
+ "php": "^8.1",
+ "psr/clock": "^1.0",
+ "symfony/clock": "^6.3.12 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6.3 || ^4.0",
+ "doctrine/orm": "^2.15.2 || ^3.0",
+ "friendsofphp/php-cs-fixer": "^v3.87.1",
+ "kylekatarnls/multi-tester": "^2.5.3",
+ "phpmd/phpmd": "^2.15.0",
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^2.1.22",
+ "phpunit/phpunit": "^10.5.53",
+ "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev",
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbonphp.github.io/carbon/",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html",
+ "issues": "https://github.com/CarbonPHP/carbon/issues",
+ "source": "https://github.com/CarbonPHP/carbon"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-09T18:23:49+00:00"
+ },
+ {
+ "name": "nette/schema",
+ "version": "v1.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/schema.git",
+ "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002",
+ "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^4.0",
+ "php": "8.1 - 8.5"
+ },
+ "require-dev": {
+ "nette/phpstan-rules": "^1.0",
+ "nette/tester": "^2.6",
+ "phpstan/extension-installer": "^1.4@stable",
+ "phpstan/phpstan": "^2.1.39@stable",
+ "tracy/tracy": "^2.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "📐 Nette Schema: validating data structures against a given Schema.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "config",
+ "nette"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/schema/issues",
+ "source": "https://github.com/nette/schema/tree/v1.3.5"
+ },
+ "time": "2026-02-23T03:47:12+00:00"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/b043439dbdf954e6c28b5ea7e34b0100f83165e0",
+ "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.2 - 8.5"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "^1.2",
+ "nette/phpstan-rules": "^1.0",
+ "nette/tester": "^2.5",
+ "phpstan/extension-installer": "^1.4@stable",
+ "phpstan/phpstan": "^2.1@stable",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::chr(), ord() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.1.5"
+ },
+ "time": "2026-07-17T23:02:45+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0"
+ },
+ "time": "2026-07-04T14:30:18+00:00"
+ },
+ {
+ "name": "nunomaduro/collision",
+ "version": "v8.9.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/collision.git",
+ "reference": "fb53eacd509a1d303858e2d20cfebf2d630254ec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/fb53eacd509a1d303858e2d20cfebf2d630254ec",
+ "reference": "fb53eacd509a1d303858e2d20cfebf2d630254ec",
+ "shasum": ""
+ },
+ "require": {
+ "filp/whoops": "^2.18.4",
+ "nunomaduro/termwind": "^2.4.0",
+ "php": "^8.2.0",
+ "symfony/console": "^7.4.14 || ^8.1.1"
+ },
+ "conflict": {
+ "laravel/framework": "<11.48.0 || >=14.0.0",
+ "phpunit/phpunit": "<11.5.50 || >=14.0.0"
+ },
+ "require-dev": {
+ "brianium/paratest": "^7.8.5",
+ "larastan/larastan": "^3.10.0",
+ "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.20.0",
+ "laravel/pint": "^1.29.3",
+ "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.3.5",
+ "pestphp/pest": "^3.8.5 || ^4.7.5 || ^5.0.0",
+ "sebastian/environment": "^7.2.1 || ^8.1.2 || ^9.3.2"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-8.x": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "./src/Adapters/Phpunit/Autoload.php"
+ ],
+ "psr-4": {
+ "NunoMaduro\\Collision\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Cli error handling for console/command-line PHP applications.",
+ "keywords": [
+ "artisan",
+ "cli",
+ "command-line",
+ "console",
+ "dev",
+ "error",
+ "handling",
+ "laravel",
+ "laravel-zero",
+ "php",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/collision/issues",
+ "source": "https://github.com/nunomaduro/collision"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2026-07-15T19:09:14+00:00"
+ },
+ {
+ "name": "nunomaduro/termwind",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/termwind.git",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^8.2",
+ "symfony/console": "^7.4.4 || ^8.0.4"
+ },
+ "require-dev": {
+ "illuminate/console": "^11.47.0",
+ "laravel/pint": "^1.27.1",
+ "mockery/mockery": "^1.6.12",
+ "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2",
+ "phpstan/phpstan": "^1.12.32",
+ "phpstan/phpstan-strict-rules": "^1.6.2",
+ "symfony/var-dumper": "^7.3.5 || ^8.0.4",
+ "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Termwind\\Laravel\\TermwindServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Termwind\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "It's like Tailwind CSS, but for the console.",
+ "keywords": [
+ "cli",
+ "console",
+ "css",
+ "package",
+ "php",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/termwind/issues",
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/xiCO2k",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T23:10:27+00:00"
+ },
+ {
+ "name": "pestphp/pest",
+ "version": "v4.7.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest.git",
+ "reference": "5dc49a71d63602a9b98fed0f2017c4679ef9f8e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest/zipball/5dc49a71d63602a9b98fed0f2017c4679ef9f8e0",
+ "reference": "5dc49a71d63602a9b98fed0f2017c4679ef9f8e0",
+ "shasum": ""
+ },
+ "require": {
+ "brianium/paratest": "^7.20.0",
+ "composer/xdebug-handler": "^3.0.5",
+ "nunomaduro/collision": "^8.9.4",
+ "nunomaduro/termwind": "^2.4.0",
+ "pestphp/pest-plugin": "^4.0.0",
+ "pestphp/pest-plugin-arch": "^4.0.2",
+ "pestphp/pest-plugin-mutate": "^4.0.1",
+ "pestphp/pest-plugin-profanity": "^4.2.1",
+ "php": "^8.3.0",
+ "phpunit/phpunit": "^12.5.30",
+ "symfony/process": "^7.4.13|^8.1.0"
+ },
+ "conflict": {
+ "filp/whoops": "<2.18.3",
+ "phpunit/phpunit": ">12.5.30",
+ "sebastian/exporter": "<7.0.0",
+ "webmozart/assert": "<1.11.0"
+ },
+ "require-dev": {
+ "mrpunyapal/peststan": "^0.2.11",
+ "pestphp/pest-dev-tools": "^4.1.0",
+ "pestphp/pest-plugin-browser": "^4.3.1",
+ "pestphp/pest-plugin-type-coverage": "^4.0.4",
+ "psy/psysh": "^0.12.24"
+ },
+ "bin": [
+ "bin/pest"
+ ],
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Mutate\\Plugins\\Mutate",
+ "Pest\\Plugins\\Configuration",
+ "Pest\\Plugins\\Bail",
+ "Pest\\Plugins\\Cache",
+ "Pest\\Plugins\\Coverage",
+ "Pest\\Plugins\\Init",
+ "Pest\\Plugins\\Environment",
+ "Pest\\Plugins\\Help",
+ "Pest\\Plugins\\Memory",
+ "Pest\\Plugins\\Only",
+ "Pest\\Plugins\\Printer",
+ "Pest\\Plugins\\ProcessIsolation",
+ "Pest\\Plugins\\Profile",
+ "Pest\\Plugins\\Retry",
+ "Pest\\Plugins\\Snapshot",
+ "Pest\\Plugins\\Verbose",
+ "Pest\\Plugins\\Version",
+ "Pest\\Plugins\\Shard",
+ "Pest\\Plugins\\Tia",
+ "Pest\\Plugins\\Parallel"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php",
+ "src/Pest.php"
+ ],
+ "psr-4": {
+ "Pest\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "The elegant PHP Testing Framework.",
+ "keywords": [
+ "framework",
+ "pest",
+ "php",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "issues": "https://github.com/pestphp/pest/issues",
+ "source": "https://github.com/pestphp/pest/tree/v4.7.5"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2026-07-06T17:06:29+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin",
+ "version": "v4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin.git",
+ "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568",
+ "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0.0",
+ "composer-runtime-api": "^2.2.2",
+ "php": "^8.3"
+ },
+ "conflict": {
+ "pestphp/pest": "<4.0.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.8.10",
+ "pestphp/pest": "^4.0.0",
+ "pestphp/pest-dev-tools": "^4.0.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Pest\\Plugin\\Manager"
+ },
+ "autoload": {
+ "psr-4": {
+ "Pest\\Plugin\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Pest plugin manager",
+ "keywords": [
+ "framework",
+ "manager",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2025-08-20T12:35:58+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin-arch",
+ "version": "v4.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-arch.git",
+ "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/3fb0d02a91b9da504b139dc7ab2a31efb7c3215c",
+ "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c",
+ "shasum": ""
+ },
+ "require": {
+ "pestphp/pest-plugin": "^4.0.0",
+ "php": "^8.3",
+ "ta-tikoma/phpunit-architecture-test": "^0.8.7"
+ },
+ "require-dev": {
+ "pestphp/pest": "^4.4.6",
+ "pestphp/pest-dev-tools": "^4.1.0"
+ },
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Arch\\Plugin"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Autoload.php"
+ ],
+ "psr-4": {
+ "Pest\\Arch\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Arch plugin for Pest PHP.",
+ "keywords": [
+ "arch",
+ "architecture",
+ "framework",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2026-04-10T17:20:19+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin-mutate",
+ "version": "v4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-mutate.git",
+ "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c",
+ "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.6.1",
+ "pestphp/pest-plugin": "^4.0.0",
+ "php": "^8.3",
+ "psr/simple-cache": "^3.0.0"
+ },
+ "require-dev": {
+ "pestphp/pest": "^4.0.0",
+ "pestphp/pest-dev-tools": "^4.0.0",
+ "pestphp/pest-plugin-type-coverage": "^4.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Pest\\Mutate\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ },
+ {
+ "name": "Sandro Gehri",
+ "email": "sandrogehri@gmail.com"
+ }
+ ],
+ "description": "Mutates your code to find untested cases",
+ "keywords": [
+ "framework",
+ "mutate",
+ "mutation",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/gehrisandro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-21T20:19:25+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin-profanity",
+ "version": "v4.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-profanity.git",
+ "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27",
+ "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27",
+ "shasum": ""
+ },
+ "require": {
+ "pestphp/pest-plugin": "^4.0.0",
+ "php": "^8.3"
+ },
+ "require-dev": {
+ "faissaloux/pest-plugin-inside": "^1.9",
+ "pestphp/pest": "^4.0.0",
+ "pestphp/pest-dev-tools": "^4.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Profanity\\Plugin"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Pest\\Profanity\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Pest Profanity Plugin",
+ "keywords": [
+ "framework",
+ "pest",
+ "php",
+ "plugin",
+ "profanity",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1"
+ },
+ "time": "2025-12-08T00:13:17+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "php-stubs/wordpress-stubs",
+ "version": "v6.9.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-stubs/wordpress-stubs.git",
+ "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5",
+ "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5",
+ "shasum": ""
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "5.6.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "nikic/php-parser": "^5.5",
+ "php": "^7.4 || ^8.0",
+ "php-stubs/generator": "^0.8.6",
+ "phpdocumentor/reflection-docblock": "^6.0",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^9.5",
+ "symfony/polyfill-php80": "*",
+ "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1",
+ "wp-coding-standards/wpcs": "3.1.0 as 2.3.0"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Pure PHP implementation of libsodium",
+ "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WordPress function and class declaration stubs for static analysis.",
+ "homepage": "https://github.com/php-stubs/wordpress-stubs",
+ "keywords": [
+ "PHPStan",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/php-stubs/wordpress-stubs/issues",
+ "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4"
+ },
+ "time": "2026-05-01T20:36:01+00:00"
+ },
+ {
+ "name": "php-stubs/wp-cli-stubs",
+ "version": "v2.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-stubs/wp-cli-stubs.git",
+ "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/af16401e299a3fd2229bd0fa9a037638a4174a9d",
+ "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d",
+ "shasum": ""
+ },
+ "require": {
+ "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0"
+ },
+ "require-dev": {
+ "php": "~7.3 || ~8.0",
+ "php-stubs/generator": "^0.8.0"
+ },
+ "suggest": {
+ "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WP-CLI function and class declaration stubs for static analysis.",
+ "homepage": "https://github.com/php-stubs/wp-cli-stubs",
+ "keywords": [
+ "PHPStan",
+ "static analysis",
+ "wordpress",
+ "wp-cli"
+ ],
+ "support": {
+ "issues": "https://github.com/php-stubs/wp-cli-stubs/issues",
+ "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.12.0"
+ },
+ "time": "2025-06-10T09:58:05+00:00"
+ },
+ {
+ "name": "phpcompatibility/php-compatibility",
+ "version": "9.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
+ "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
+ "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
+ },
+ "conflict": {
+ "squizlabs/php_codesniffer": "2.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "homepage": "https://github.com/wimg",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
+ }
+ ],
+ "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
+ "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibility"
+ },
+ "time": "2019-12-27T09:44:58+00:00"
+ },
+ {
+ "name": "phpcompatibility/phpcompatibility-paragonie",
+ "version": "1.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
+ "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
+ "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
+ "shasum": ""
+ },
+ "require": {
+ "phpcompatibility/php-compatibility": "^9.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "paragonie/random_compat": "dev-master",
+ "paragonie/sodium_compat": "dev-master"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "lead"
+ }
+ ],
+ "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
+ "homepage": "http://phpcompatibility.com/",
+ "keywords": [
+ "compatibility",
+ "paragonie",
+ "phpcs",
+ "polyfill",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
+ "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCompatibility",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcompatibility",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-09-19T17:43:28+00:00"
+ },
+ {
+ "name": "phpcompatibility/phpcompatibility-wp",
+ "version": "2.1.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
+ "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa",
+ "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa",
+ "shasum": ""
+ },
+ "require": {
+ "phpcompatibility/php-compatibility": "^9.0",
+ "phpcompatibility/phpcompatibility-paragonie": "^1.0",
+ "squizlabs/php_codesniffer": "^3.3"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "lead"
+ }
+ ],
+ "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
+ "homepage": "http://phpcompatibility.com/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
+ "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCompatibility",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcompatibility",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-10-18T00:05:59+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsextra",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/39467533fdb742446d68c1d10ac33d625ee0311c",
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "phpcsstandards/phpcsutils": "^1.2.3",
+ "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcsstandards/phpcsdevcs": "^1.2.0",
+ "phpcsstandards/phpcsdevtools": "^1.2.1",
+ "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
+ }
+ ],
+ "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
+ "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHPCSExtra"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-07-27T11:13:17+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsutils",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ },
+ "require-dev": {
+ "ext-filter": "*",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcsstandards/phpcsdevcs": "^1.2.0",
+ "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHPCSUtils/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
+ }
+ ],
+ "description": "A suite of utility functions for use with PHP_CodeSniffer",
+ "homepage": "https://phpcsutils.com/",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "phpcs3",
+ "phpcs4",
+ "standards",
+ "static analysis",
+ "tokens",
+ "utility"
+ ],
+ "support": {
+ "docs": "https://phpcsutils.com/",
+ "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
+ "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHPCSUtils"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-07-27T10:28:41+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "6.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582",
+ "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.1",
+ "ext-filter": "*",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^2.0",
+ "phpstan/phpdoc-parser": "^2.0",
+ "webmozart/assert": "^1.9.1 || ^2"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.5 || ~1.6.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "psalm/phar": "^5.26",
+ "shipmonk/dead-code-detector": "^0.5.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3"
+ },
+ "time": "2026-03-18T20:49:53+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9",
+ "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.5",
+ "psalm/phar": "^4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev",
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0"
+ },
+ "time": "2026-01-06T21:53:42+00:00"
+ },
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.9.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
+ "reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-27T19:41:33+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "2.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
+ "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^5.3.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3"
+ },
+ "time": "2026-07-08T07:01:06+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.2.7",
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/692db47b9dddb0487934e5236e77d48594aef921",
+ "reference": "692db47b9dddb0487934e5236e77d48594aef921",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ondřej Mirtes"
+ },
+ {
+ "name": "Markus Staab"
+ },
+ {
+ "name": "Vincent Langlet"
+ }
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2026-07-29T17:39:32+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-deprecation-rules",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
+ "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/67bedd65c24bc72840afc45aed48b1059dd44bec",
+ "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.39"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "shipmonk/name-collision-detector": "^2.1"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
+ "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5"
+ },
+ "time": "2026-07-22T06:50:43+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "12.5.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "186dab580576598076de6818596d12b61801880e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/186dab580576598076de6818596d12b61801880e",
+ "reference": "186dab580576598076de6818596d12b61801880e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.3",
+ "phpunit/php-text-template": "^5.0",
+ "sebastian/complexity": "^5.0",
+ "sebastian/environment": "^8.1.2",
+ "sebastian/lines-of-code": "^4.0.1",
+ "sebastian/version": "^6.0",
+ "theseer/tokenizer": "^2.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.28"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.5.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.7"
+ },
+ "funding": [
+ {
+ "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/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-01T13:24:19+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+ "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "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/php-file-iterator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-02-02T14:04:18+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^12.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:58+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:16+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:38+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "12.5.30",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/900400a5b616d6fb306f9549f6da33ba615d3fbb",
+ "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.3",
+ "phpunit/php-code-coverage": "^12.5.7",
+ "phpunit/php-file-iterator": "^6.0.1",
+ "phpunit/php-invoker": "^6.0.0",
+ "phpunit/php-text-template": "^5.0.0",
+ "phpunit/php-timer": "^8.0.0",
+ "sebastian/cli-parser": "^4.2.1",
+ "sebastian/comparator": "^7.1.8",
+ "sebastian/diff": "^7.0.0",
+ "sebastian/environment": "^8.1.2",
+ "sebastian/exporter": "^7.0.3",
+ "sebastian/global-state": "^8.0.3",
+ "sebastian/object-enumerator": "^7.0.0",
+ "sebastian/recursion-context": "^7.0.1",
+ "sebastian/type": "^6.0.4",
+ "sebastian/version": "^6.0.0",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.30"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsoring.html",
+ "type": "other"
+ }
+ ],
+ "time": "2026-06-15T13:12:30+00:00"
+ },
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "ramsey/collection",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "captainhook/plugin-composer": "^5.3",
+ "ergebnis/composer-normalize": "^2.45",
+ "fakerphp/faker": "^1.24",
+ "hamcrest/hamcrest-php": "^2.0",
+ "jangregor/phpstan-prophecy": "^2.1",
+ "mockery/mockery": "^1.6",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpspec/prophecy-phpunit": "^2.3",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^10.5",
+ "ramsey/coding-standard": "^2.3",
+ "ramsey/conventional-commits": "^1.6",
+ "roave/security-advisories": "dev-latest"
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ },
+ "ramsey/conventional-commits": {
+ "configFile": "conventional-commits.json"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ramsey\\Collection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
+ }
+ ],
+ "description": "A PHP library for representing and manipulating collections.",
+ "keywords": [
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/2.1.1"
+ },
+ "time": "2025-03-22T05:38:12+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "4.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8",
+ "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": ">=0.8.16 <=0.18",
+ "php": "^8.0",
+ "ramsey/collection": "^1.2 || ^2.0"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "captainhook/captainhook": "^5.25",
+ "captainhook/plugin-composer": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "ergebnis/composer-normalize": "^2.47",
+ "mockery/mockery": "^1.6",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock": "^2.6",
+ "php-mock/php-mock-mockery": "^1.5",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpbench/phpbench": "^1.2.14",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "slevomat/coding-standard": "^8.18",
+ "squizlabs/php_codesniffer": "^3.13"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "source": "https://github.com/ramsey/uuid/tree/4.9.3"
+ },
+ "time": "2026-06-18T03:57:49+00:00"
+ },
+ {
+ "name": "rector/rector",
+ "version": "2.5.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "861c77fd2a6d45317a401f4e0b617f947e8b6f96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/861c77fd2a6d45317a401f4e0b617f947e8b6f96",
+ "reference": "861c77fd2a6d45317a401f4e0b617f947e8b6f96",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0",
+ "phpstan/phpstan": "^2.2.6"
+ },
+ "conflict": {
+ "rector/rector-doctrine": "*",
+ "rector/rector-downgrade-php": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-symfony": "*"
+ },
+ "suggest": {
+ "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
+ },
+ "bin": [
+ "bin/rector"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "homepage": "https://getrector.com/",
+ "keywords": [
+ "automation",
+ "dev",
+ "migration",
+ "refactoring"
+ ],
+ "support": {
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/2.5.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2026-07-27T06:19:16+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "4.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "7d05781b13f7dec9043a629a21d086ed74582a15"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15",
+ "reference": "7d05781b13f7dec9043a629a21d086ed74582a15",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1"
+ },
+ "funding": [
+ {
+ "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/sebastian/cli-parser",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-17T05:29:34+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "7.1.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "7c65c1e79836812819705b473a90c12399542485"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485",
+ "reference": "7c65c1e79836812819705b473a90c12399542485",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/diff": "^7.0",
+ "sebastian/exporter": "^7.0.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8"
+ },
+ "funding": [
+ {
+ "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/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-21T04:45:25+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:25+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0",
+ "symfony/process": "^7.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:46+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "8.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439",
+ "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.26"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2"
+ },
+ "funding": [
+ {
+ "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/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-25T13:40:20+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "7.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
+ "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/recursion-context": "^7.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3"
+ },
+ "funding": [
+ {
+ "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/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-20T04:37:17+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "8.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b164d3274d6537ab462591c5755f76a8f5b1aae9",
+ "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0.1"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^12.5.28"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.3"
+ },
+ "funding": [
+ {
+ "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/sebastian/global-state",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-01T15:10:33+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e",
+ "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "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/sebastian/lines-of-code",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-19T16:22:07+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:57:48+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:17+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+ "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "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/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T04:44:59+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "6.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "82ff822c2edc46724be9f7411d3163021f602773"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773",
+ "reference": "82ff822c2edc46724be9f7411d3163021f602773",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/6.0.4"
+ },
+ "funding": [
+ {
+ "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/sebastian/type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-20T06:45:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T05:00:38+00:00"
+ },
+ {
+ "name": "sirbrillig/phpcs-variable-analysis",
+ "version": "v2.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git",
+ "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369",
+ "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
+ "phpstan/phpstan": "^1.7 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3",
+ "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "autoload": {
+ "psr-4": {
+ "VariableAnalysis\\": "VariableAnalysis/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sam Graham",
+ "email": "php-codesniffer-variableanalysis@illusori.co.uk"
+ },
+ {
+ "name": "Payton Swick",
+ "email": "payton@foolord.com"
+ }
+ ],
+ "description": "A PHPCS sniff to detect problems with variables.",
+ "keywords": [
+ "phpcs",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues",
+ "source": "https://github.com/sirbrillig/phpcs-variable-analysis",
+ "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki"
+ },
+ "time": "2025-09-30T22:22:48+00:00"
+ },
+ {
+ "name": "slevomat/coding-standard",
+ "version": "8.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec",
+ "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpdoc-parser": "^2.3.0",
+ "squizlabs/php_codesniffer": "^3.13.4"
+ },
+ "require-dev": {
+ "phing/phing": "3.0.1|3.1.0",
+ "php-parallel-lint/php-parallel-lint": "1.4.0",
+ "phpstan/phpstan": "2.1.24",
+ "phpstan/phpstan-deprecation-rules": "2.0.3",
+ "phpstan/phpstan-phpunit": "2.0.7",
+ "phpstan/phpstan-strict-rules": "2.0.6",
+ "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "keywords": [
+ "dev",
+ "phpcs"
],
"support": {
- "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
- "source": "https://github.com/PHPCompatibility/PHPCompatibility"
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.22.1"
},
- "time": "2019-12-27T09:44:58+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/kukulich",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-13T08:53:30+00:00"
},
{
- "name": "phpcompatibility/phpcompatibility-paragonie",
- "version": "1.3.4",
+ "name": "spatie/backtrace",
+ "version": "1.8.2",
"source": {
"type": "git",
- "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
- "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf"
+ "url": "https://github.com/spatie/backtrace.git",
+ "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
- "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
+ "url": "https://api.github.com/repos/spatie/backtrace/zipball/8ffe78be5ed355b5009e3dd989d183433e9a5adc",
+ "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc",
"shasum": ""
},
"require": {
- "phpcompatibility/php-compatibility": "^9.0"
+ "php": "^7.3 || ^8.0"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
- "paragonie/random_compat": "dev-master",
- "paragonie/sodium_compat": "dev-master"
+ "ext-json": "*",
+ "laravel/serializable-closure": "^1.3 || ^2.0",
+ "phpunit/phpunit": "^9.3 || ^11.4.3",
+ "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6",
+ "symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0"
},
- "suggest": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Backtrace\\": "src"
+ }
},
- "type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
- "LGPL-3.0-or-later"
+ "MIT"
],
"authors": [
{
- "name": "Wim Godden",
- "role": "lead"
- },
- {
- "name": "Juliette Reinders Folmer",
- "role": "lead"
+ "name": "Freek Van de Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
- "homepage": "http://phpcompatibility.com/",
+ "description": "A better backtrace",
+ "homepage": "https://github.com/spatie/backtrace",
"keywords": [
- "compatibility",
- "paragonie",
- "phpcs",
- "polyfill",
- "standards",
- "static analysis"
+ "Backtrace",
+ "spatie"
],
"support": {
- "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
- "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy",
- "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
+ "issues": "https://github.com/spatie/backtrace/issues",
+ "source": "https://github.com/spatie/backtrace/tree/1.8.2"
},
"funding": [
{
- "url": "https://github.com/PHPCompatibility",
- "type": "github"
- },
- {
- "url": "https://github.com/jrfnl",
+ "url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
- },
- {
- "url": "https://thanks.dev/u/gh/phpcompatibility",
- "type": "thanks_dev"
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "other"
}
],
- "time": "2025-09-19T17:43:28+00:00"
+ "time": "2026-03-11T13:48:28+00:00"
},
{
- "name": "phpcompatibility/phpcompatibility-wp",
- "version": "2.1.8",
+ "name": "spatie/phpunit-snapshot-assertions",
+ "version": "5.4.0",
"source": {
"type": "git",
- "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
- "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa"
+ "url": "https://github.com/spatie/phpunit-snapshot-assertions.git",
+ "reference": "b5ad3efab36e6003c491bb42fb997b226760d5a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa",
- "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa",
+ "url": "https://api.github.com/repos/spatie/phpunit-snapshot-assertions/zipball/b5ad3efab36e6003c491bb42fb997b226760d5a0",
+ "reference": "b5ad3efab36e6003c491bb42fb997b226760d5a0",
"shasum": ""
},
"require": {
- "phpcompatibility/php-compatibility": "^9.0",
- "phpcompatibility/phpcompatibility-paragonie": "^1.0",
- "squizlabs/php_codesniffer": "^3.3"
+ "composer-runtime-api": "^2.0",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "php": "^8.1",
+ "phpunit/phpunit": "^9.6|^10.0|^11.0|^12.0|^13.0",
+ "symfony/property-access": "^5.2|^6.2|^7.0|^8.0",
+ "symfony/serializer": "^5.2|^6.2|^7.0|^8.0",
+ "symfony/yaml": "^5.2|^6.2|^7.0|^8.0"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0"
+ "spatie/pixelmatch-php": "dev-main",
+ "spatie/ray": "^1.37"
},
"suggest": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ "spatie/pixelmatch-php": "Required to use the image snapshot assertions"
+ },
+ "bin": [
+ "bin/update-snapshots"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-v5": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Snapshots\\": "src"
+ }
},
- "type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
- "LGPL-3.0-or-later"
+ "MIT"
],
"authors": [
{
- "name": "Wim Godden",
- "role": "lead"
- },
- {
- "name": "Juliette Reinders Folmer",
- "role": "lead"
+ "name": "Sebastian De Deyne",
+ "email": "sebastian@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
}
],
- "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
- "homepage": "http://phpcompatibility.com/",
+ "description": "Snapshot testing with PHPUnit",
+ "homepage": "https://github.com/spatie/phpunit-snapshot-assertions",
"keywords": [
- "compatibility",
- "phpcs",
- "standards",
- "static analysis",
- "wordpress"
+ "assert",
+ "phpunit",
+ "phpunit-snapshot-assertions",
+ "snapshot",
+ "spatie",
+ "testing"
],
"support": {
- "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
- "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy",
- "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
+ "issues": "https://github.com/spatie/phpunit-snapshot-assertions/issues",
+ "source": "https://github.com/spatie/phpunit-snapshot-assertions/tree/5.4.0"
},
"funding": [
{
- "url": "https://github.com/PHPCompatibility",
- "type": "github"
- },
- {
- "url": "https://github.com/jrfnl",
- "type": "github"
- },
- {
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
- },
- {
- "url": "https://thanks.dev/u/gh/phpcompatibility",
- "type": "thanks_dev"
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
}
],
- "time": "2025-10-18T00:05:59+00:00"
+ "time": "2026-04-29T09:52:03+00:00"
},
{
- "name": "phpcsstandards/phpcsextra",
- "version": "1.5.1",
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.5",
"source": {
"type": "git",
- "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
- "reference": "39467533fdb742446d68c1d10ac33d625ee0311c"
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/39467533fdb742446d68c1d10ac33d625ee0311c",
- "reference": "39467533fdb742446d68c1d10ac33d625ee0311c",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
"shasum": ""
},
"require": {
- "php": ">=5.4",
- "phpcsstandards/phpcsutils": "^1.2.3",
- "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
},
"require-dev": {
- "php-parallel-lint/php-console-highlighter": "^1.0",
- "php-parallel-lint/php-parallel-lint": "^1.4.0",
- "phpcsstandards/phpcsdevcs": "^1.2.0",
- "phpcsstandards/phpcsdevtools": "^1.2.1",
- "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
- },
- "type": "phpcodesniffer-standard",
- "extra": {
- "branch-alias": {
- "dev-stable": "1.x-dev",
- "dev-develop": "1.x-dev"
- }
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
},
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
- "LGPL-3.0-or-later"
+ "BSD-3-Clause"
],
"authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
{
"name": "Juliette Reinders Folmer",
- "homepage": "https://github.com/jrfnl",
- "role": "lead"
+ "role": "Current lead"
},
{
"name": "Contributors",
- "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
}
],
- "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
"keywords": [
- "PHP_CodeSniffer",
- "phpcbf",
- "phpcodesniffer-standard",
"phpcs",
"standards",
"static analysis"
],
"support": {
- "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
- "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy",
- "source": "https://github.com/PHPCSStandards/PHPCSExtra"
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
},
"funding": [
{
@@ -1987,171 +7696,260 @@
"type": "thanks_dev"
}
],
- "time": "2026-07-27T11:13:17+00:00"
+ "time": "2025-11-04T16:30:35+00:00"
},
{
- "name": "phpcsstandards/phpcsutils",
- "version": "1.2.3",
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
"source": {
"type": "git",
- "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
- "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f"
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/5f35d9408c54d7b529501f3c688b6eae562aea1f",
- "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
"shasum": ""
},
"require": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
- "php": ">=5.4",
- "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
},
"require-dev": {
- "ext-filter": "*",
- "php-parallel-lint/php-console-highlighter": "^1.0",
- "php-parallel-lint/php-parallel-lint": "^1.4.0",
- "phpcsstandards/phpcsdevcs": "^1.2.0",
- "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0"
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
},
- "type": "phpcodesniffer-standard",
- "extra": {
- "branch-alias": {
- "dev-stable": "1.x-dev",
- "dev-develop": "1.x-dev"
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
}
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "symfony/clock",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
},
+ "type": "library",
"autoload": {
- "classmap": [
- "PHPCSUtils/"
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "LGPL-3.0-or-later"
+ "MIT"
],
"authors": [
{
- "name": "Juliette Reinders Folmer",
- "homepage": "https://github.com/jrfnl",
- "role": "lead"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "Contributors",
- "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "A suite of utility functions for use with PHP_CodeSniffer",
- "homepage": "https://phpcsutils.com/",
+ "description": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com",
"keywords": [
- "PHP_CodeSniffer",
- "phpcbf",
- "phpcodesniffer-standard",
- "phpcs",
- "phpcs3",
- "phpcs4",
- "standards",
- "static analysis",
- "tokens",
- "utility"
+ "clock",
+ "psr20",
+ "time"
],
"support": {
- "docs": "https://phpcsutils.com/",
- "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
- "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy",
- "source": "https://github.com/PHPCSStandards/PHPCSUtils"
+ "source": "https://github.com/symfony/clock/tree/v7.4.8"
},
"funding": [
{
- "url": "https://github.com/PHPCSStandards",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://github.com/jrfnl",
+ "url": "https://github.com/fabpot",
"type": "github"
},
{
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/phpcsstandards",
- "type": "thanks_dev"
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
}
],
- "time": "2026-07-27T10:28:41+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
- "name": "phpstan/phpdoc-parser",
- "version": "2.3.3",
+ "name": "symfony/console",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3"
+ "url": "https://github.com/symfony/console.git",
+ "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
- "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
+ "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930",
+ "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930",
"shasum": ""
},
"require": {
- "php": "^7.4 || ^8.0"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
- "doctrine/annotations": "^2.0",
- "nikic/php-parser": "^5.3.0",
- "php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^2.0",
- "phpstan/phpstan-phpunit": "^2.0",
- "phpstan/phpstan-strict-rules": "^2.0",
- "phpunit/phpunit": "^9.6",
- "symfony/process": "^5.2"
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/lock": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
- "PHPStan\\PhpDocParser\\": [
- "src/"
- ]
- }
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
"support": {
- "issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3"
+ "source": "https://github.com/symfony/console/tree/v7.4.15"
},
- "time": "2026-07-08T07:01:06+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2026-07-27T13:51:00+00:00"
},
{
- "name": "phpstan/phpstan",
- "version": "2.2.7",
+ "name": "symfony/css-selector",
+ "version": "v7.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "b75663ed96cf4756e28e3105476f220f92886cc4"
+ },
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/692db47b9dddb0487934e5236e77d48594aef921",
- "reference": "692db47b9dddb0487934e5236e77d48594aef921",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/b75663ed96cf4756e28e3105476f220f92886cc4",
+ "reference": "b75663ed96cf4756e28e3105476f220f92886cc4",
"shasum": ""
},
"require": {
- "php": "^7.4|^8.0"
- },
- "conflict": {
- "phpstan/phpstan-shim": "*"
+ "php": ">=8.2"
},
- "bin": [
- "phpstan",
- "phpstan.phar"
- ],
"type": "library",
"autoload": {
- "files": [
- "bootstrap.php"
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -2160,1774 +7958,2339 @@
],
"authors": [
{
- "name": "Ondřej Mirtes"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
- "name": "Markus Staab"
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
},
{
- "name": "Vincent Langlet"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "PHPStan - PHP Static Analysis Tool",
- "keywords": [
- "dev",
- "static analysis"
- ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
"support": {
- "docs": "https://phpstan.org/user-guide/getting-started",
- "forum": "https://github.com/phpstan/phpstan/discussions",
- "issues": "https://github.com/phpstan/phpstan/issues",
- "security": "https://github.com/phpstan/phpstan/security/policy",
- "source": "https://github.com/phpstan/phpstan-src"
+ "source": "https://github.com/symfony/css-selector/tree/v7.4.9"
},
"funding": [
{
- "url": "https://github.com/ondrejmirtes",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
"type": "github"
},
{
- "url": "https://github.com/phpstan",
+ "url": "https://github.com/nicolas-grekas",
"type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
}
],
- "time": "2026-07-29T17:39:32+00:00"
+ "time": "2026-04-18T13:18:21+00:00"
},
{
- "name": "phpstan/phpstan-deprecation-rules",
- "version": "2.0.5",
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.7.1",
"source": {
"type": "git",
- "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
- "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec"
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/67bedd65c24bc72840afc45aed48b1059dd44bec",
- "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
"shasum": ""
},
"require": {
- "php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.1.39"
- },
- "require-dev": {
- "php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-phpunit": "^2.0",
- "phpunit/phpunit": "^9.6",
- "shipmonk/name-collision-detector": "^2.1"
+ "php": ">=8.1"
},
- "type": "phpstan-extension",
+ "type": "library",
"extra": {
- "phpstan": {
- "includes": [
- "rules.neon"
- ]
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
}
},
"autoload": {
- "psr-4": {
- "PHPStan\\": "src/"
- }
+ "files": [
+ "function.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
- "keywords": [
- "static analysis"
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
- "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
},
- "time": "2026-07-22T06:50:43+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2026-06-05T06:23:12+00:00"
},
{
- "name": "phpunit/php-code-coverage",
- "version": "9.2.32",
+ "name": "symfony/dom-crawler",
+ "version": "v7.4.12",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5"
+ "url": "https://github.com/symfony/dom-crawler.git",
+ "reference": "b59b59122690976550fd142c23fab62c84738db6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5",
- "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b59b59122690976550fd142c23fab62c84738db6",
+ "reference": "b59b59122690976550fd142c23fab62c84738db6",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-libxml": "*",
- "ext-xmlwriter": "*",
- "nikic/php-parser": "^4.19.1 || ^5.1.0",
- "php": ">=7.3",
- "phpunit/php-file-iterator": "^3.0.6",
- "phpunit/php-text-template": "^2.0.4",
- "sebastian/code-unit-reverse-lookup": "^2.0.3",
- "sebastian/complexity": "^2.0.3",
- "sebastian/environment": "^5.1.5",
- "sebastian/lines-of-code": "^1.0.4",
- "sebastian/version": "^3.0.2",
- "theseer/tokenizer": "^1.2.3"
+ "masterminds/html5": "^2.6",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
- "phpunit/phpunit": "^9.6"
- },
- "suggest": {
- "ext-pcov": "PHP extension that provides line coverage",
- "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ "symfony/css-selector": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "9.2.x-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\DomCrawler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
- "keywords": [
- "coverage",
- "testing",
- "xunit"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
],
+ "description": "Eases DOM navigation for HTML and XML documents",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32"
+ "source": "https://github.com/symfony/dom-crawler/tree/v7.4.12"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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-08-22T04:23:01+00:00"
+ "time": "2026-05-20T07:20:23+00:00"
},
{
- "name": "phpunit/php-file-iterator",
- "version": "3.0.6",
+ "name": "symfony/error-handler",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
- "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/d49f6a19f326db41ae7103bdc38e3eb35a791261",
+ "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/polyfill-php85": "^1.32",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
+ },
+ "conflict": {
+ "symfony/deprecation-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4"
},
"require-dev": {
- "phpunit/phpunit": "^9.3"
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4|^7.0|^8.0",
+ "symfony/webpack-encore-bundle": "^1.0|^2.0"
},
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "FilterIterator implementation that filters files based on a list of suffixes.",
- "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
- "keywords": [
- "filesystem",
- "iterator"
- ],
+ "description": "Provides tools to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ "source": "https://github.com/symfony/error-handler/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2021-12-02T12:48:52+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
- "name": "phpunit/php-invoker",
- "version": "3.1.1",
+ "name": "symfony/event-dispatcher",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-invoker.git",
- "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
- "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/336e7f3b9e95aba04f93ea9143920c2186abfbb9",
+ "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
},
- "require-dev": {
- "ext-pcntl": "*",
- "phpunit/phpunit": "^9.3"
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
},
- "suggest": {
- "ext-pcntl": "*"
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.1-dev"
- }
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/framework-bundle": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
},
+ "type": "library",
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Invoke callables with a timeout",
- "homepage": "https://github.com/sebastianbergmann/php-invoker/",
- "keywords": [
- "process"
- ],
+ "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": {
- "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
- "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-09-28T05:58:55+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
- "name": "phpunit/php-text-template",
- "version": "2.0.4",
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.7.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
- "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e",
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e",
"shasum": ""
},
"require": {
- "php": ">=7.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
},
"type": "library",
"extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
"branch-alias": {
- "dev-master": "2.0-dev"
+ "dev-main": "3.7-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Simple template engine.",
- "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
"keywords": [
- "template"
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
- "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-10-26T05:33:50+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
- "name": "phpunit/php-timer",
- "version": "5.0.3",
+ "name": "symfony/http-foundation",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
- "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1f898ee8188adda9417fb52cf8425a8342c254e7",
+ "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "^1.1"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.6",
+ "symfony/cache": "<6.4.12|>=7.0,<7.1.5"
},
"require-dev": {
- "phpunit/phpunit": "^9.3"
+ "doctrine/dbal": "^3.6|^4",
+ "predis/predis": "^1.1|^2.0",
+ "symfony/cache": "^6.4.12|^7.1.5|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^6.4|^7.0|^8.0",
+ "symfony/rate-limiter": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Utility class for timing",
- "homepage": "https://github.com/sebastianbergmann/php-timer/",
- "keywords": [
- "timer"
- ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/php-timer/issues",
- "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-10-26T13:16:10+00:00"
+ "time": "2026-07-29T07:12:33+00:00"
},
{
- "name": "phpunit/phpunit",
- "version": "9.6.35",
+ "name": "symfony/http-kernel",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b"
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "403275d94f94d5626c3288c599b3b48093ba24f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0edba2f3a0c48df3553cb9b640810b30df60302b",
- "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/403275d94f94d5626c3288c599b3b48093ba24f7",
+ "reference": "403275d94f94d5626c3288c599b3b48093ba24f7",
"shasum": ""
},
"require": {
- "doctrine/instantiator": "^1.5.0 || ^2",
- "ext-dom": "*",
- "ext-filter": "*",
- "ext-json": "*",
- "ext-libxml": "*",
- "ext-mbstring": "*",
- "ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.13.4",
- "phar-io/manifest": "^2.0.4",
- "phar-io/version": "^3.2.1",
- "php": ">=7.3",
- "phpunit/php-code-coverage": "^9.2.32",
- "phpunit/php-file-iterator": "^3.0.6",
- "phpunit/php-invoker": "^3.1.1",
- "phpunit/php-text-template": "^2.0.4",
- "phpunit/php-timer": "^5.0.3",
- "sebastian/cli-parser": "^1.0.2",
- "sebastian/code-unit": "^1.0.8",
- "sebastian/comparator": "^4.0.10",
- "sebastian/diff": "^4.0.6",
- "sebastian/environment": "^5.1.5",
- "sebastian/exporter": "^4.0.8",
- "sebastian/global-state": "^5.0.8",
- "sebastian/object-enumerator": "^4.0.4",
- "sebastian/resource-operations": "^3.0.4",
- "sebastian/type": "^3.2.1",
- "sebastian/version": "^3.0.2"
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^7.3|^8.0",
+ "symfony/http-foundation": "^7.4|^8.0",
+ "symfony/polyfill-ctype": "^1.8"
},
- "suggest": {
- "ext-soap": "To be able to generate mocks based on WSDL files",
- "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ "conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/cache": "<6.4",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/doctrine-bridge": "<6.4",
+ "symfony/flex": "<2.10",
+ "symfony/form": "<6.4",
+ "symfony/http-client": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/mailer": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/translation": "<6.4",
+ "symfony/translation-contracts": "<2.5",
+ "symfony/twig-bridge": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/var-dumper": "<6.4",
+ "twig/twig": "<3.12"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
},
- "bin": [
- "phpunit"
- ],
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "9.6-dev"
- }
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^6.4|^7.0|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/css-selector": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0",
+ "symfony/dom-crawler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
+ "symfony/http-client-contracts": "^2.5|^3",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^7.1|^8.0",
+ "symfony/routing": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^7.1|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/translation": "^6.4|^7.0|^8.0",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "symfony/validator": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12|^4.0"
},
+ "type": "library",
"autoload": {
- "files": [
- "src/Framework/Assert/Functions.php"
- ],
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "The PHP Unit Testing framework.",
- "homepage": "https://phpunit.de/",
- "keywords": [
- "phpunit",
- "testing",
- "xunit"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
],
+ "description": "Provides a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.35"
+ "source": "https://github.com/symfony/http-kernel/tree/v7.4.15"
},
"funding": [
{
- "url": "https://phpunit.de/sponsoring.html",
- "type": "other"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2026-07-06T14:48:07+00:00"
+ "time": "2026-07-29T11:40:42+00:00"
},
{
- "name": "rector/rector",
- "version": "2.5.8",
+ "name": "symfony/mime",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/rectorphp/rector.git",
- "reference": "861c77fd2a6d45317a401f4e0b617f947e8b6f96"
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/rectorphp/rector/zipball/861c77fd2a6d45317a401f4e0b617f947e8b6f96",
- "reference": "861c77fd2a6d45317a401f4e0b617f947e8b6f96",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
+ "reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"shasum": ""
},
"require": {
- "php": "^7.4|^8.0",
- "phpstan/phpstan": "^2.2.6"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
- "rector/rector-doctrine": "*",
- "rector/rector-downgrade-php": "*",
- "rector/rector-phpunit": "*",
- "rector/rector-symfony": "*"
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<5.2|>=7",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
},
- "suggest": {
- "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^6.4|^7.0|^8.0",
+ "symfony/property-info": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4.3|^7.0.3|^8.0"
},
- "bin": [
- "bin/rector"
- ],
"type": "library",
"autoload": {
- "files": [
- "bootstrap.php"
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "Instant Upgrade and Automated Refactoring of any PHP code",
- "homepage": "https://getrector.com/",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
"keywords": [
- "automation",
- "dev",
- "migration",
- "refactoring"
+ "mime",
+ "mime-type"
],
"support": {
- "issues": "https://github.com/rectorphp/rector/issues",
- "source": "https://github.com/rectorphp/rector/tree/2.5.8"
+ "source": "https://github.com/symfony/mime/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/tomasvotruba",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2026-07-27T06:19:16+00:00"
+ "time": "2026-07-29T07:59:49+00:00"
},
{
- "name": "sebastian/cli-parser",
- "version": "1.0.2",
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.37.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/cli-parser.git",
- "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
- "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=7.2"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Library for parsing CLI options",
- "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
- "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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-03-02T06:27:43+00:00"
+ "time": "2026-04-10T16:19:22+00:00"
},
{
- "name": "sebastian/code-unit",
- "version": "1.0.8",
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.41.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit.git",
- "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
- "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=7.2"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Collection of value objects that represent the PHP code units",
- "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/code-unit/issues",
- "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-10-26T13:08:54+00:00"
+ "time": "2026-07-28T08:25:59+00:00"
},
{
- "name": "sebastian/code-unit-reverse-lookup",
- "version": "2.0.3",
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.38.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "dc21118016c039a66235cf93d96b435ffb282412"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
- "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
+ "reference": "dc21118016c039a66235cf93d96b435ffb282412",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=7.2",
+ "symfony/polyfill-intl-normalizer": "^1.10"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "2.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Looks up which function or method a line of code belongs to",
- "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
- "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-09-28T05:30:19+00:00"
+ "time": "2026-05-25T15:22:23+00:00"
},
{
- "name": "sebastian/comparator",
- "version": "4.0.10",
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.38.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d"
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d",
- "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"shasum": ""
},
"require": {
- "php": ">=7.3",
- "sebastian/diff": "^4.0",
- "sebastian/exporter": "^4.0"
+ "php": ">=7.2"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
"classmap": [
- "src/"
+ "Resources/stubs"
]
},
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides the functionality to compare PHP values for equality",
- "homepage": "https://github.com/sebastianbergmann/comparator",
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
"keywords": [
- "comparator",
- "compare",
- "equality"
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/comparator/issues",
- "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2026-01-24T09:22:56+00:00"
+ "time": "2026-05-25T13:48:31+00:00"
},
{
- "name": "sebastian/complexity",
- "version": "2.0.3",
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.38.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/complexity.git",
- "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
- "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
"shasum": ""
},
"require": {
- "nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=7.3"
+ "ext-iconv": "*",
+ "php": ">=7.2"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "2.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "classmap": [
- "src/"
- ]
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Library for calculating the complexity of PHP code units",
- "homepage": "https://github.com/sebastianbergmann/complexity",
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/complexity/issues",
- "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2023-12-22T06:19:30+00:00"
+ "time": "2026-05-27T06:59:30+00:00"
},
{
- "name": "sebastian/diff",
- "version": "4.0.6",
+ "name": "symfony/polyfill-php80",
+ "version": "v1.37.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
- "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
"shasum": ""
},
"require": {
- "php": ">=7.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.3",
- "symfony/process": "^4.2 || ^5"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
"classmap": [
- "src/"
+ "Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
},
{
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Diff implementation",
- "homepage": "https://github.com/sebastianbergmann/diff",
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
"keywords": [
- "diff",
- "udiff",
- "unidiff",
- "unified diff"
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/diff/issues",
- "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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-03-02T06:30:58+00:00"
+ "time": "2026-04-10T16:19:22+00:00"
},
{
- "name": "sebastian/environment",
- "version": "5.1.5",
+ "name": "symfony/polyfill-php83",
+ "version": "v1.41.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
- "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/5ea99087fb99c273a9b9236ed4c31e78b16103c6",
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6",
"shasum": ""
},
"require": {
- "php": ">=7.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.3"
- },
- "suggest": {
- "ext-posix": "*"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php83\\": ""
+ },
"classmap": [
- "src/"
+ "Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides functionality to handle HHVM/PHP environments",
- "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
"keywords": [
- "Xdebug",
- "environment",
- "hhvm"
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/environment/issues",
- "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.41.0"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2023-02-03T06:03:51+00:00"
+ "time": "2026-07-01T12:47:55+00:00"
},
{
- "name": "sebastian/exporter",
- "version": "4.0.8",
+ "name": "symfony/process",
+ "version": "v7.4.13",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c"
+ "url": "https://github.com/symfony/process.git",
+ "reference": "f5804be144caceb570f6747519999636b664f24c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c",
- "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c",
+ "reference": "f5804be144caceb570f6747519999636b664f24c",
"shasum": ""
},
"require": {
- "php": ">=7.3",
- "sebastian/recursion-context": "^4.0"
- },
- "require-dev": {
- "ext-mbstring": "*",
- "phpunit/phpunit": "^9.3"
+ "php": ">=8.2"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
- "name": "Bernhard Schussek",
- "email": "bschussek@gmail.com"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "https://www.github.com/sebastianbergmann/exporter",
- "keywords": [
- "export",
- "exporter"
- ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8"
+ "source": "https://github.com/symfony/process/tree/v7.4.13"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-09-24T06:03:27+00:00"
+ "time": "2026-05-23T16:05:06+00:00"
},
{
- "name": "sebastian/global-state",
- "version": "5.0.8",
+ "name": "symfony/property-access",
+ "version": "v7.4.8",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6"
+ "url": "https://github.com/symfony/property-access.git",
+ "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
- "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "url": "https://api.github.com/repos/symfony/property-access/zipball/b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc",
+ "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc",
"shasum": ""
},
"require": {
- "php": ">=7.3",
- "sebastian/object-reflector": "^2.0",
- "sebastian/recursion-context": "^4.0"
+ "php": ">=8.2",
+ "symfony/property-info": "^6.4.32|~7.3.10|^7.4.4|^8.0.4"
},
"require-dev": {
- "ext-dom": "*",
- "phpunit/phpunit": "^9.3"
- },
- "suggest": {
- "ext-uopz": "*"
+ "symfony/cache": "^6.4|^7.0|^8.0",
+ "symfony/var-exporter": "^6.4.1|^7.0.1|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\PropertyAccess\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Snapshotting of global state",
- "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "description": "Provides functions to read and write from/to an object or array using a simple string notation",
+ "homepage": "https://symfony.com",
"keywords": [
- "global state"
+ "access",
+ "array",
+ "extraction",
+ "index",
+ "injection",
+ "object",
+ "property",
+ "property-path",
+ "reflection"
],
"support": {
- "issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8"
+ "source": "https://github.com/symfony/property-access/tree/v7.4.8"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-10T07:10:35+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
- "name": "sebastian/lines-of-code",
- "version": "1.0.4",
+ "name": "symfony/property-info",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/lines-of-code.git",
- "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
+ "url": "https://github.com/symfony/property-info.git",
+ "reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
- "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "url": "https://api.github.com/repos/symfony/property-info/zipball/fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad",
+ "reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad",
"shasum": ""
},
"require": {
- "nikic/php-parser": "^4.18 || ^5.0",
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/string": "^6.4|^7.0|^8.0",
+ "symfony/type-info": "^7.4.7|^8.0.7"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "<5.2|>=7",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/cache": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/serializer": "<6.4"
},
"require-dev": {
- "phpunit/phpunit": "^9.3"
+ "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+ "phpstan/phpdoc-parser": "^1.0|^2.0",
+ "symfony/cache": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\PropertyInfo\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Kévin Dunglas",
+ "email": "dunglas@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Library for counting the lines of code in PHP source code",
- "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "description": "Extracts information about PHP class' properties using metadata of popular sources",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "doctrine",
+ "phpdoc",
+ "property",
+ "symfony",
+ "type",
+ "validator"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
- "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
+ "source": "https://github.com/symfony/property-info/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2023-12-22T06:20:34+00:00"
+ "time": "2026-07-28T07:09:44+00:00"
},
{
- "name": "sebastian/object-enumerator",
- "version": "4.0.4",
+ "name": "symfony/routing",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
- "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/80c0a93d3f8e7499f716204a1fb38ead942a7a2b",
+ "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b",
"shasum": ""
},
"require": {
- "php": ">=7.3",
- "sebastian/object-reflector": "^2.0",
- "sebastian/recursion-context": "^4.0"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/yaml": "<6.4"
},
"require-dev": {
- "phpunit/phpunit": "^9.3"
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Traverses array structures and object graphs to enumerate all referenced objects",
- "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "description": "Maps an HTTP request to a set of configuration variables",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
- "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ "source": "https://github.com/symfony/routing/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-10-26T13:12:34+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
- "name": "sebastian/object-reflector",
- "version": "2.0.4",
+ "name": "symfony/serializer",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+ "url": "https://github.com/symfony/serializer.git",
+ "reference": "917f1575bec2853f45e012d8718f518365a9d258"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
- "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/917f1575bec2853f45e012d8718f518365a9d258",
+ "reference": "917f1575bec2853f45e012d8718f518365a9d258",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-php84": "^1.30"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "<5.2|>=7",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/property-access": "<6.4.31|>=7.0,<7.4.2|>=8.0,<8.0.2",
+ "symfony/property-info": "<6.4.43",
+ "symfony/type-info": "<7.2.5",
+ "symfony/uid": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/yaml": "<6.4"
},
"require-dev": {
- "phpunit/phpunit": "^9.3"
+ "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+ "phpstan/phpdoc-parser": "^1.0|^2.0",
+ "seld/jsonlint": "^1.10",
+ "symfony/cache": "^6.4|^7.0|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^7.2|^8.0",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/filesystem": "^6.4|^7.0|^8.0",
+ "symfony/form": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^6.4.31|^7.4.2|^8.0.2",
+ "symfony/property-info": "^6.4.43|^7.4.15|^8.0.15",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/type-info": "^7.2.5|^8.0",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "symfony/validator": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Component\\Serializer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Allows reflection of object attributes, including inherited and non-public ones",
- "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
- "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ "source": "https://github.com/symfony/serializer/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-10-26T13:14:26+00:00"
+ "time": "2026-07-29T07:59:49+00:00"
},
{
- "name": "sebastian/recursion-context",
- "version": "4.0.6",
+ "name": "symfony/service-contracts",
+ "version": "v3.7.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "539c6691e0623af6dc6f9c20384c120f963465a0"
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0",
- "reference": "539c6691e0623af6dc6f9c20384c120f963465a0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
},
- "require-dev": {
- "phpunit/phpunit": "^9.3"
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
},
"type": "library",
"extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
"branch-alias": {
- "dev-master": "4.0-dev"
+ "dev-main": "3.7-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides functionality to recursively process PHP variables",
- "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.7.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://liberapay.com/sebastianbergmann",
- "type": "liberapay"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/sebastianbergmann",
- "type": "thanks_dev"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-10T06:57:39+00:00"
+ "time": "2026-06-16T09:55:08+00:00"
},
{
- "name": "sebastian/resource-operations",
- "version": "3.0.4",
+ "name": "symfony/string",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
+ "url": "https://github.com/symfony/string.git",
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
- "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b",
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.33",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "phpunit/phpunit": "^9.0"
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.0-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Provides a list of PHP built-in functions that operate on resources",
- "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
"support": {
- "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
+ "source": "https://github.com/symfony/string/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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-03-14T16:00:52+00:00"
+ "time": "2026-07-28T07:33:02+00:00"
},
{
- "name": "sebastian/type",
- "version": "3.2.1",
+ "name": "symfony/translation",
+ "version": "v7.4.14",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/type.git",
- "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
- "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.5.3|^3.3"
+ },
+ "conflict": {
+ "nikic/php-parser": "<5.0",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/service-contracts": "<2.5",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3|3.0"
},
"require-dev": {
- "phpunit/phpunit": "^9.5"
+ "nikic/php-parser": "^5.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
+ "symfony/http-client-contracts": "^2.5|^3.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/routing": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.2-dev"
- }
- },
"autoload": {
- "classmap": [
- "src/"
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Collection of value objects that represent the types of the PHP type system",
- "homepage": "https://github.com/sebastianbergmann/type",
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/sebastianbergmann/type/issues",
- "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
+ "source": "https://github.com/symfony/translation/tree/v7.4.14"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2023-02-03T06:13:03+00:00"
+ "time": "2026-06-06T09:33:19+00:00"
},
{
- "name": "sebastian/version",
- "version": "3.0.2",
+ "name": "symfony/translation-contracts",
+ "version": "v3.7.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/version.git",
- "reference": "c6c1022351a901512170118436c764e473f6de8c"
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
- "reference": "c6c1022351a901512170118436c764e473f6de8c",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621",
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621",
"shasum": ""
},
"require": {
- "php": ">=7.3"
+ "php": ">=8.1"
},
"type": "library",
"extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
"branch-alias": {
- "dev-master": "3.0-dev"
+ "dev-main": "3.7-dev"
}
},
"autoload": {
- "classmap": [
- "src/"
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Library that helps with managing the version number of Git-hosted PHP projects",
- "homepage": "https://github.com/sebastianbergmann/version",
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
"support": {
- "issues": "https://github.com/sebastianbergmann/version/issues",
- "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1"
},
"funding": [
{
- "url": "https://github.com/sebastianbergmann",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2020-09-28T06:39:44+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
- "name": "sirbrillig/phpcs-variable-analysis",
- "version": "v2.13.0",
+ "name": "symfony/type-info",
+ "version": "v7.4.9",
"source": {
"type": "git",
- "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git",
- "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369"
+ "url": "https://github.com/symfony/type-info.git",
+ "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369",
- "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369",
+ "url": "https://api.github.com/repos/symfony/type-info/zipball/cafeedbf157b890e94ac5b83eaed85595106d5d6",
+ "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6",
"shasum": ""
},
"require": {
- "php": ">=5.4.0",
- "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0"
+ "php": ">=8.2",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "phpstan/phpdoc-parser": "<1.30"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
- "phpstan/phpstan": "^1.7 || ^2.0",
- "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3",
- "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0"
+ "phpstan/phpdoc-parser": "^1.30|^2.0"
},
- "type": "phpcodesniffer-standard",
+ "type": "library",
"autoload": {
"psr-4": {
- "VariableAnalysis\\": "VariableAnalysis/"
- }
+ "Symfony\\Component\\TypeInfo\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-2-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Sam Graham",
- "email": "php-codesniffer-variableanalysis@illusori.co.uk"
+ "name": "Mathias Arlaud",
+ "email": "mathias.arlaud@gmail.com"
},
{
- "name": "Payton Swick",
- "email": "payton@foolord.com"
+ "name": "Baptiste LEDUC",
+ "email": "baptiste.leduc@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "A PHPCS sniff to detect problems with variables.",
+ "description": "Extracts PHP types information.",
+ "homepage": "https://symfony.com",
"keywords": [
- "phpcs",
- "static analysis"
+ "PHPStan",
+ "phpdoc",
+ "symfony",
+ "type"
],
"support": {
- "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues",
- "source": "https://github.com/sirbrillig/phpcs-variable-analysis",
- "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki"
+ "source": "https://github.com/symfony/type-info/tree/v7.4.9"
},
- "time": "2025-09-30T22:22:48+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "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": "2026-04-22T15:21:55+00:00"
},
{
- "name": "slevomat/coding-standard",
- "version": "8.22.1",
+ "name": "symfony/var-dumper",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/slevomat/coding-standard.git",
- "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec"
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec",
- "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
"shasum": ""
},
"require": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2",
- "php": "^7.4 || ^8.0",
- "phpstan/phpdoc-parser": "^2.3.0",
- "squizlabs/php_codesniffer": "^3.13.4"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0"
},
- "require-dev": {
- "phing/phing": "3.0.1|3.1.0",
- "php-parallel-lint/php-parallel-lint": "1.4.0",
- "phpstan/phpstan": "2.1.24",
- "phpstan/phpstan-deprecation-rules": "2.0.3",
- "phpstan/phpstan-phpunit": "2.0.7",
- "phpstan/phpstan-strict-rules": "2.0.6",
- "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10"
+ "conflict": {
+ "symfony/console": "<6.4"
},
- "type": "phpcodesniffer-standard",
- "extra": {
- "branch-alias": {
- "dev-master": "8.x-dev"
- }
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12|^4.0"
},
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
"autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
"psr-4": {
- "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
- }
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
"keywords": [
- "dev",
- "phpcs"
+ "debug",
+ "dump"
],
"support": {
- "issues": "https://github.com/slevomat/coding-standard/issues",
- "source": "https://github.com/slevomat/coding-standard/tree/8.22.1"
+ "source": "https://github.com/symfony/var-dumper/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/kukulich",
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
"type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-09-13T08:53:30+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
- "name": "squizlabs/php_codesniffer",
- "version": "3.13.5",
+ "name": "symfony/yaml",
+ "version": "v7.4.15",
"source": {
"type": "git",
- "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
- "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4"
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
- "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/e101850ded5d2c0d44bf32abb8996404afec2dec",
+ "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec",
"shasum": ""
},
"require": {
- "ext-simplexml": "*",
- "ext-tokenizer": "*",
- "ext-xmlwriter": "*",
- "php": ">=5.4.0"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
},
"require-dev": {
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ "symfony/console": "^6.4|^7.0|^8.0"
},
"bin": [
- "bin/phpcbf",
- "bin/phpcs"
+ "Resources/bin/yaml-lint"
],
"type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
- "authors": [
- {
- "name": "Greg Sherwood",
- "role": "Former lead"
- },
+ "authors": [
{
- "name": "Juliette Reinders Folmer",
- "role": "Current lead"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
- "name": "Contributors",
- "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
- "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
- "keywords": [
- "phpcs",
- "standards",
- "static analysis"
- ],
+ "description": "Loads and dumps YAML files",
+ "homepage": "https://symfony.com",
"support": {
- "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
- "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
- "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
- "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ "source": "https://github.com/symfony/yaml/tree/v7.4.15"
},
"funding": [
{
- "url": "https://github.com/PHPCSStandards",
- "type": "github"
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "url": "https://github.com/jrfnl",
+ "url": "https://github.com/fabpot",
"type": "github"
},
{
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
},
{
- "url": "https://thanks.dev/u/gh/phpcsstandards",
- "type": "thanks_dev"
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
}
],
- "time": "2025-11-04T16:30:35+00:00"
+ "time": "2026-07-21T15:13:06+00:00"
},
{
"name": "szepeviktor/phpstan-wordpress",
@@ -3992,25 +10355,84 @@
},
"time": "2025-09-14T02:58:22+00:00"
},
+ {
+ "name": "ta-tikoma/phpunit-architecture-test",
+ "version": "0.8.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git",
+ "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8",
+ "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18.0 || ^5.0.0",
+ "php": "^8.1.0",
+ "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0",
+ "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0",
+ "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0"
+ },
+ "require-dev": {
+ "laravel/pint": "^1.13.7",
+ "phpstan/phpstan": "^1.10.52"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPUnit\\Architecture\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ni Shi",
+ "email": "futik0ma011@gmail.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Methods for testing application architecture",
+ "keywords": [
+ "architecture",
+ "phpunit",
+ "stucture",
+ "test",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues",
+ "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7"
+ },
+ "time": "2026-02-17T17:25:14+00:00"
+ },
{
"name": "theseer/tokenizer",
- "version": "1.3.1",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
- "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
+ "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
- "php": "^7.2 || ^8.0"
+ "php": "^8.1"
},
"type": "library",
"autoload": {
@@ -4032,7 +10454,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ "source": "https://github.com/theseer/tokenizer/tree/2.0.1"
},
"funding": [
{
@@ -4040,7 +10462,7 @@
"type": "github"
}
],
- "time": "2025-11-17T20:03:58+00:00"
+ "time": "2025-12-08T11:19:18+00:00"
},
{
"name": "tomasvotruba/type-coverage",
@@ -4100,133 +10522,294 @@
"time": "2026-05-26T08:14:01+00:00"
},
{
- "name": "wp-coding-standards/wpcs",
- "version": "3.4.1",
+ "name": "vlucas/phpdotenv",
+ "version": "v5.6.4",
"source": {
"type": "git",
- "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
- "reference": "ec2ff942335f33683a5957a85d138753876a05cf"
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/ec2ff942335f33683a5957a85d138753876a05cf",
- "reference": "ec2ff942335f33683a5957a85d138753876a05cf",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/416df702837983f8d5ff48c9c3fee4f5f57b980b",
+ "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b",
"shasum": ""
},
"require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.1.4",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.5",
+ "symfony/polyfill-ctype": "^1.26",
+ "symfony/polyfill-mbstring": "^1.26",
+ "symfony/polyfill-php80": "^1.26"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
"ext-filter": "*",
- "ext-libxml": "*",
- "ext-tokenizer": "*",
- "ext-xmlreader": "*",
- "php": ">=7.2",
- "phpcsstandards/phpcsextra": "^1.5.1",
- "phpcsstandards/phpcsutils": "^1.2.3",
- "squizlabs/php_codesniffer": "^3.13.5"
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "5.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "https://github.com/vlucas"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-06T19:11:50+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "8e1051fe39379367aecf014f41744ce7539a856f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f",
+ "reference": "8e1051fe39379367aecf014f41744ce7539a856f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
},
"require-dev": {
- "php-parallel-lint/php-console-highlighter": "^1.0.0",
- "php-parallel-lint/php-parallel-lint": "^1.4.0",
- "phpcompatibility/php-compatibility": "^10.0.0@dev",
- "phpcsstandards/phpcsdevtools": "^1.2.0",
- "phpunit/phpunit": "^8.0 || ^9.0"
+ "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5"
},
"suggest": {
- "ext-iconv": "For improved results",
- "ext-mbstring": "For improved results"
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
},
- "type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
- "name": "Contributors",
- "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
+ "name": "Lars Moelleken",
+ "homepage": "https://www.moelleken.org/"
}
],
- "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
"keywords": [
- "phpcs",
- "standards",
- "static analysis",
- "wordpress"
+ "ascii",
+ "clean",
+ "php"
],
"support": {
- "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
- "source": "https://github.com/WordPress/WordPress-Coding-Standards",
- "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.1.1"
},
"funding": [
{
- "url": "https://opencollective.com/php_codesniffer",
+ "url": "https://www.paypal.me/moelleken",
"type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
}
],
- "time": "2026-07-27T11:53:23+00:00"
+ "time": "2026-04-26T05:33:54+00:00"
},
{
- "name": "yoast/phpunit-polyfills",
- "version": "2.0.5",
+ "name": "webmozart/assert",
+ "version": "2.4.1",
"source": {
"type": "git",
- "url": "https://github.com/Yoast/PHPUnit-Polyfills.git",
- "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27"
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27",
- "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70",
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70",
"shasum": ""
},
"require": {
- "php": ">=5.6",
- "phpunit/phpunit": "^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0"
+ "ext-ctype": "*",
+ "ext-date": "*",
+ "ext-filter": "*",
+ "php": "^8.2"
},
- "require-dev": {
- "php-parallel-lint/php-console-highlighter": "^1.0.0",
- "php-parallel-lint/php-parallel-lint": "^1.4.0",
- "yoast/yoastcs": "^3.2.0"
+ "suggest": {
+ "ext-intl": "",
+ "ext-simplexml": "",
+ "ext-spl": ""
},
"type": "library",
"extra": {
+ "psalm": {
+ "pluginClass": "Webmozart\\Assert\\PsalmPlugin"
+ },
"branch-alias": {
- "dev-main": "4.x-dev"
+ "dev-master": "2.0-dev",
+ "dev-feature/2-0": "2.0-dev"
}
},
"autoload": {
- "files": [
- "phpunitpolyfills-autoload.php"
- ]
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Team Yoast",
- "email": "support@yoast.com",
- "homepage": "https://yoast.com"
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
},
+ {
+ "name": "Woody Gilk",
+ "email": "woody.gilk@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/2.4.1"
+ },
+ "time": "2026-06-15T15:31:57+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "3.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/ec2ff942335f33683a5957a85d138753876a05cf",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "ext-libxml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlreader": "*",
+ "php": ">=7.2",
+ "phpcsstandards/phpcsextra": "^1.5.1",
+ "phpcsstandards/phpcsutils": "^1.2.3",
+ "squizlabs/php_codesniffer": "^3.13.5"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^10.0.0@dev",
+ "phpcsstandards/phpcsdevtools": "^1.2.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
+ },
+ "suggest": {
+ "ext-iconv": "For improved results",
+ "ext-mbstring": "For improved results"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
"name": "Contributors",
- "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors"
+ "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
- "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests",
- "homepage": "https://github.com/Yoast/PHPUnit-Polyfills",
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
"keywords": [
- "phpunit",
- "polyfill",
- "testing"
+ "phpcs",
+ "standards",
+ "static analysis",
+ "wordpress"
],
"support": {
- "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues",
- "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy",
- "source": "https://github.com/Yoast/PHPUnit-Polyfills"
+ "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
+ "source": "https://github.com/WordPress/WordPress-Coding-Standards",
+ "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
- "time": "2025-08-10T05:13:49+00:00"
+ "funding": [
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "custom"
+ }
+ ],
+ "time": "2026-07-27T11:53:23+00:00"
}
],
"aliases": [],
diff --git a/phpcs.xml b/phpcs.xml
index 59bfa01..693f4d5 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -29,6 +29,15 @@
*/tests/*
+
+
+ */tests/*
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index fe0829d..bd85503 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -4,25 +4,31 @@
bootstrap="tests/bootstrap.php"
backupGlobals="false"
colors="true"
- convertErrorsToExceptions="true"
- convertNoticesToExceptions="true"
- convertWarningsToExceptions="true"
- xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
+ xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
>
-
-
- ./src/
-
+
+
+ ./tests/Unit/
+
-
-
-
-
-
+
+ ./tests/Integration/
+
-
-
- ./tests/
+
+ ./tests/Arch/
+
+
+
+
+ ./src/
+
+
diff --git a/tests/Arch/ConventionsTest.php b/tests/Arch/ConventionsTest.php
new file mode 100644
index 0000000..542fa6e
--- /dev/null
+++ b/tests/Arch/ConventionsTest.php
@@ -0,0 +1,24 @@
+expect( 'TenupFramework' )
+ ->toUseStrictTypes();
+
+arch( 'ships no debugging leftovers' )
+ ->expect( [ 'var_dump', 'var_export', 'print_r', 'dd', 'dump', 'error_log' ] )
+ ->not->toBeUsed()
+ // BlockRegistrar::register_blocks() logs deliberately when the block editor is missing,
+ // which is a real diagnostic rather than a leftover. Scoping the exception to that one
+ // class keeps the rule useful: a new error_log() anywhere else still fails.
+ ->ignoring( 'TenupFramework\BlockRegistrar' );
+
+arch( 'never depends on test code' )
+ ->expect( 'TenupFramework' )
+ ->not->toUse( [ 'TenupFrameworkTests', 'TenupFrameworkTestClasses' ] );
diff --git a/tests/Assets/GetAssetInfoTest.php b/tests/Assets/GetAssetInfoTest.php
deleted file mode 100644
index 415772f..0000000
--- a/tests/Assets/GetAssetInfoTest.php
+++ /dev/null
@@ -1,246 +0,0 @@
-setup_asset_vars(
- dist_path: 'dist',
- fallback_version: '1.0.0'
- );
-
- $this->assertEquals( 'dist/', $asset_info->dist_path );
- $this->assertEquals( '1.0.0', $asset_info->fallback_version );
- }
-
- /**
- * Test get_asset_info returns an array with version and dependencies.
- *
- * @return void
- */
- public function test_get_asset_info_returns_array_with_version_and_dependencies() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $asset_info->setup_asset_vars(
- dist_path: dirname( __DIR__, 2 ) . '/fixtures/assets/dist',
- fallback_version: '1.0.0'
- );
-
- $asset = $asset_info->get_asset_info(
- slug: 'test-script'
- );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/js/test-script.asset.php';
- $this->assertEquals( $vars, $asset );
-
- $asset = $asset_info->get_asset_info(
- slug: 'test-style'
- );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/css/test-style.asset.php';
- $this->assertEquals( $vars, $asset );
-
- $asset = $asset_info->get_asset_info(
- slug: 'non-existent'
- );
-
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- }
-
- /**
- * Test get_asset_info returns a string when passed a specific dependency.
- *
- * @return void
- */
- public function test_get_asset_info_returns_string_when_passed_specific_dependency() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $asset_info->setup_asset_vars(
- dist_path: dirname( __DIR__, 2 ) . '/fixtures/assets/dist',
- fallback_version: '1.0.0'
- );
-
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/js/test-script.asset.php';
-
- $version = $asset_info->get_asset_info(
- slug: 'test-script',
- attribute: 'version'
- );
-
- $this->assertEquals( $vars['version'], $version );
-
- $version = $asset_info->get_asset_info(
- slug: 'test-script',
- attribute: 'dependencies'
- );
-
- $this->assertEquals( $vars['dependencies'], $version );
- }
-
- /**
- * Test get_asset_info throws and exception when get_asset_info is called without setting up the asset vars.
- *
- * @return void
- */
- public function test_get_asset_info_throws_exception_when_called_without_setting_up_asset_vars() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $this->expectException( \RuntimeException::class );
- $this->expectExceptionMessage( 'Asset variables not set. Please run setup_asset_vars() before calling get_asset_info().' );
-
- $asset_info->get_asset_info(
- slug: 'test-script'
- );
- }
-
- /**
- * Test get_asset_info with prefix-based slug handling (css/, js/, blocks/).
- *
- * @return void
- */
- public function test_get_asset_info_with_prefix_based_slug() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $asset_info->setup_asset_vars(
- dist_path: dirname( __DIR__, 2 ) . '/fixtures/assets/dist',
- fallback_version: '1.0.0'
- );
-
- // Test CSS prefix with existing fixture
- $asset = $asset_info->get_asset_info( slug: 'css/test-style' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/css/test-style.asset.php';
- $this->assertEquals( $vars, $asset );
-
- // Test JS prefix with existing fixture
- $asset = $asset_info->get_asset_info( slug: 'js/test-script' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/js/test-script.asset.php';
- $this->assertEquals( $vars, $asset );
-
- // Test blocks prefix with existing fixture
- $asset = $asset_info->get_asset_info( slug: 'blocks/test-block' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/blocks/test-block.asset.php';
- $this->assertEquals( $vars, $asset );
- }
-
- /**
- * Test get_asset_info priority order: prefix-based slugs take priority over fallback.
- *
- * @return void
- */
- public function test_get_asset_info_priority_order_prefix_vs_fallback() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $asset_info->setup_asset_vars(
- dist_path: dirname( __DIR__, 2 ) . '/fixtures/assets/dist',
- fallback_version: '1.0.0'
- );
-
- // Test that prefix-based slug works with existing fixtures
- $asset = $asset_info->get_asset_info( slug: 'css/test-style' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/css/test-style.asset.php';
- $this->assertEquals( $vars, $asset );
-
- // Test that fallback still works for non-prefixed slugs
- $asset = $asset_info->get_asset_info( slug: 'test-script' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/js/test-script.asset.php';
- $this->assertEquals( $vars, $asset );
- }
-
- /**
- * Test get_asset_info fallback behavior when direct file doesn't exist.
- *
- * @return void
- */
- public function test_get_asset_info_fallback_when_direct_file_missing() {
- $asset_info = new class() {
- use GetAssetInfo;
- };
-
- $asset_info->setup_asset_vars(
- dist_path: dirname( __DIR__, 2 ) . '/fixtures/assets/dist',
- fallback_version: '1.0.0'
- );
-
- // Test that it falls back to JS directory first (priority order: js -> css -> blocks)
- // Using existing fixture that exists in js/ directory
- $asset = $asset_info->get_asset_info( slug: 'test-script' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/js/test-script.asset.php';
- $this->assertEquals( $vars, $asset );
-
- // Test CSS fallback with existing fixture
- $asset = $asset_info->get_asset_info( slug: 'test-style' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/css/test-style.asset.php';
- $this->assertEquals( $vars, $asset );
-
- // Test blocks fallback with existing fixture
- $asset = $asset_info->get_asset_info( slug: 'test-block' );
- $this->assertIsArray( $asset );
- $this->assertArrayHasKey( 'version', $asset );
- $this->assertArrayHasKey( 'dependencies', $asset );
- $vars = require dirname( __DIR__, 2 ) . '/fixtures/assets/dist/blocks/test-block.asset.php';
- $this->assertEquals( $vars, $asset );
- }
-}
diff --git a/tests/Bin/GenerateClassCacheTest.php b/tests/Bin/GenerateClassCacheTest.php
deleted file mode 100644
index 81d10db..0000000
--- a/tests/Bin/GenerateClassCacheTest.php
+++ /dev/null
@@ -1,232 +0,0 @@
-
- */
- private $temp_dirs = [];
-
- /**
- * Remove any temporary directories created during the test.
- *
- * @return void
- */
- protected function tearDown(): void { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
- foreach ( $this->temp_dirs as $dir ) {
- $this->remove_dir( $dir );
- }
- $this->temp_dirs = [];
-
- parent::tearDown();
- }
-
- /**
- * Running the command against a directory writes a readable cache of its classes.
- *
- * @return void
- */
- public function test_generates_a_cache_for_a_directory() {
- $dir = $this->example_copy( 'plugin-inc' );
-
- $result = $this->run_bin( [ $dir ] );
-
- $this->assertSame( 0, $result['exit'], $result['stderr'] );
- $this->assertStringContainsString( 'Cached', $result['stdout'] );
-
- $cache_file = $this->cache_file_path( $dir );
- $this->assertFileExists( $cache_file );
-
- $cached = require $cache_file;
- $this->assertContains( 'TenupFrameworkExamples\\Modules\\GreetingModule', $cached );
- $this->assertContains( 'TenupFrameworkExamples\\Support\\Formatter', $cached );
- }
-
- /**
- * With no arguments the command prints usage to stderr and exits non-zero.
- *
- * @return void
- */
- public function test_reports_usage_and_fails_without_arguments() {
- $result = $this->run_bin( [] );
-
- $this->assertSame( 1, $result['exit'] );
- $this->assertStringContainsString( 'Usage:', $result['stderr'] );
- }
-
- /**
- * A missing directory fails that directory (non-zero exit, error on stderr) but the command
- * still processes the directories that are valid.
- *
- * @return void
- */
- public function test_missing_directory_fails_but_valid_directories_still_cache() {
- $good = $this->example_copy( 'plugin-inc' );
- $missing = sys_get_temp_dir() . '/tenup_bin_missing_' . uniqid( '', true );
-
- $result = $this->run_bin( [ $missing, $good ] );
-
- $this->assertSame( 1, $result['exit'] );
- $this->assertStringContainsString( $missing, $result['stderr'] );
- $this->assertStringContainsString( 'Failed to generate cache', $result['stderr'] );
-
- // The valid directory was still cached despite the earlier failure.
- $this->assertFileExists( $this->cache_file_path( $good ) );
- }
-
- /**
- * Several directories can be cached in a single invocation.
- *
- * @return void
- */
- public function test_caches_multiple_directories() {
- $first = $this->example_copy( 'plugin-inc' );
- $second = $this->example_copy( 'second-inc' );
-
- $result = $this->run_bin( [ $first, $second ] );
-
- $this->assertSame( 0, $result['exit'], $result['stderr'] );
- $this->assertFileExists( $this->cache_file_path( $first ) );
- $this->assertFileExists( $this->cache_file_path( $second ) );
-
- $cached = require $this->cache_file_path( $second );
- $this->assertContains( 'TenupFrameworkExamples\\Widgets\\Card', $cached );
- }
-
- /**
- * Run the bin script with the given arguments, returning its stdout, stderr and exit code.
- *
- * @param array $args The arguments to pass after the script name.
- *
- * @return array{stdout: string, stderr: string, exit: int}
- */
- private function run_bin( array $args ): array {
- $script = dirname( __DIR__, 2 ) . '/bin/tenup-framework-generate-class-cache';
- $command = array_map( 'escapeshellarg', array_merge( [ PHP_BINARY, $script ], $args ) );
-
- $descriptors = [
- 1 => [ 'pipe', 'w' ],
- 2 => [ 'pipe', 'w' ],
- ];
-
- $process = proc_open( implode( ' ', $command ), $descriptors, $pipes );
- $this->assertIsResource( $process );
-
- $stdout = (string) stream_get_contents( $pipes[1] );
- $stderr = (string) stream_get_contents( $pipes[2] );
- fclose( $pipes[1] );
- fclose( $pipes[2] );
-
- $exit = proc_close( $process );
-
- return [
- 'stdout' => $stdout,
- 'stderr' => $stderr,
- 'exit' => $exit,
- ];
- }
-
- /**
- * Copy an example directory into a fresh temp directory so the command can write a cache
- * into it without touching the committed examples.
- *
- * @param string $name The example directory name under tests/examples.
- *
- * @return string The path to the temp copy.
- */
- private function example_copy( string $name ): string {
- $source = __DIR__ . '/../examples/' . $name;
- $target = sys_get_temp_dir() . '/tenup_bin_' . $name . '_' . uniqid( '', true );
-
- $this->copy_dir( $source, $target );
- $this->temp_dirs[] = $target;
-
- return $target;
- }
-
- /**
- * The absolute path to the cache file the command writes for a directory.
- *
- * @param string $dir The discovery directory.
- *
- * @return string
- */
- private function cache_file_path( string $dir ): string {
- return $dir . '/' . ModuleInitialization::CACHE_DIR_NAME . '/' . ModuleInitialization::CACHE_FILENAME;
- }
-
- /**
- * Recursively copy a directory.
- *
- * @param string $source The source directory.
- * @param string $target The target directory.
- *
- * @return void
- */
- private function copy_dir( string $source, string $target ): void {
- mkdir( $target, 0777, true );
-
- $items = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator( $source, \FilesystemIterator::SKIP_DOTS ),
- \RecursiveIteratorIterator::SELF_FIRST
- );
-
- foreach ( $items as $item ) {
- $destination = $target . '/' . $items->getSubPathname();
- if ( $item->isDir() ) {
- mkdir( $destination, 0777, true );
- } else {
- copy( $item->getPathname(), $destination );
- }
- }
- }
-
- /**
- * Recursively remove a directory.
- *
- * @param string $dir The directory to remove.
- *
- * @return void
- */
- private function remove_dir( string $dir ): void {
- if ( ! is_dir( $dir ) ) {
- return;
- }
-
- $items = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator( $dir, \FilesystemIterator::SKIP_DOTS ),
- \RecursiveIteratorIterator::CHILD_FIRST
- );
-
- foreach ( $items as $item ) {
- if ( $item->isDir() ) {
- rmdir( $item->getPathname() );
- } else {
- unlink( $item->getPathname() );
- }
- }
-
- rmdir( $dir );
- }
-}
diff --git a/tests/BlockRegistrarTest.php b/tests/BlockRegistrarTest.php
deleted file mode 100644
index 80cd0a0..0000000
--- a/tests/BlockRegistrarTest.php
+++ /dev/null
@@ -1,346 +0,0 @@
-assertInstanceOf( \TenupFramework\ModuleInterface::class, $block_registrar );
- $this->assertInstanceOf( \TenupFramework\BlockRegistrar::class, $block_registrar );
- }
-
- /**
- * Test that can_register returns true by default.
- *
- * @return void
- */
- public function test_can_register_returns_true() {
- $block_registrar = new TestBlockRegistrar();
-
- $this->assertTrue( $block_registrar->can_register() );
- }
-
- /**
- * Test that get_blocks_directory is abstract and must be implemented.
- *
- * @return void
- */
- public function test_get_blocks_directory_is_abstract() {
- $this->expectException( \Error::class );
- new \TenupFramework\BlockRegistrar();
- }
-
- /**
- * Test that register method calls parent register and adds hooks.
- *
- * @return void
- */
- public function test_register_method_adds_hooks() {
- // Create a concrete test class
- $block_registrar = new TestBlockRegistrar();
-
- // This should not throw an exception
- $block_registrar->register();
- $this->assertTrue( true ); // If we get here, register() worked
- }
-
- /**
- * Test that get_blocks_directory returns an array.
- *
- * @return void
- */
- public function test_get_blocks_directory_returns_array() {
- $block_registrar = new TestBlockRegistrar();
- $directories = $block_registrar->get_blocks_directory();
-
- $this->assertIsArray( $directories );
- $this->assertCount( 1, $directories );
- $this->assertEquals( '/test/blocks/', $directories[0] );
- }
-
- /**
- * Test that multiple directories can be returned.
- *
- * @return void
- */
- public function test_multiple_directories_support() {
- $block_registrar = new TestMultiDirectoryBlockRegistrar();
- $directories = $block_registrar->get_blocks_directory();
-
- $this->assertIsArray( $directories );
- $this->assertCount( 3, $directories );
- $this->assertEquals( '/test/blocks/', $directories[0] );
- $this->assertEquals( '/test/custom-blocks/', $directories[1] );
- $this->assertEquals( '/test/vendor-blocks/', $directories[2] );
- }
-
- /**
- * Test that empty directory array is handled correctly.
- *
- * @return void
- */
- public function test_empty_directory_array_support() {
- $block_registrar = new TestEmptyDirectoryBlockRegistrar();
- $directories = $block_registrar->get_blocks_directory();
-
- $this->assertIsArray( $directories );
- $this->assertEmpty( $directories );
- }
-
- /**
- * Test register_blocks with non-existent directories.
- *
- * @return void
- */
- public function test_register_blocks_with_non_existent_directories() {
- $block_registrar = new TestBlockRegistrar();
-
- // This test verifies the method exists and can be called
- // In a real WordPress environment, it would handle non-existent directories gracefully
- $this->assertTrue( method_exists( $block_registrar, 'register_blocks' ) );
- }
-
- /**
- * Test register_blocks with empty directory array.
- *
- * @return void
- */
- public function test_register_blocks_with_empty_directory_array() {
- $block_registrar = new TestEmptyDirectoryBlockRegistrar();
-
- // This test verifies the method exists and can be called
- // In a real WordPress environment, it would handle empty directories gracefully
- $this->assertTrue( method_exists( $block_registrar, 'register_blocks' ) );
- }
-
- /**
- * Test get_block_options without markup.php file.
- *
- * @return void
- */
- public function test_get_block_options_without_markup() {
- $block_registrar = new TestBlockRegistrar();
-
- // Use reflection to access protected method
- $reflection = new \ReflectionClass( $block_registrar );
- $method = $reflection->getMethod( 'get_block_options' );
- $method->setAccessible( true );
-
- $options = $method->invoke( $block_registrar, '/test/block-without-markup/' );
-
- $this->assertIsArray( $options );
- $this->assertEmpty( $options );
- }
-
- /**
- * Test get_block_options with markup.php file.
- *
- * @return void
- */
- public function test_get_block_options_with_markup() {
- $block_registrar = new TestBlockRegistrar();
-
- // Use reflection to access protected method
- $reflection = new \ReflectionClass( $block_registrar );
- $method = $reflection->getMethod( 'get_block_options' );
- $method->setAccessible( true );
-
- // Mock file_exists to return true for markup.php
- $original_file_exists = 'file_exists';
- if ( function_exists( 'file_exists' ) ) {
- // In a real test environment, you'd mock this properly
- // For now, we'll test the structure
- $options = $method->invoke( $block_registrar, '/test/block-with-markup/' );
-
- // The method should return an array (empty if file doesn't exist)
- $this->assertIsArray( $options );
- }
- }
-
- /**
- * Test register_allowed_block_types method.
- *
- * @return void
- */
- public function test_register_allowed_block_types() {
- $block_registrar = new TestBlockRegistrar();
-
- // Use reflection to access protected method
- $reflection = new \ReflectionClass( $block_registrar );
- $method = $reflection->getMethod( 'register_allowed_block_types' );
- $method->setAccessible( true );
-
- $block_names = [ 'test/block1', 'test/block2' ];
-
- // This should not throw an exception
- $method->invoke( $block_registrar, $block_names );
- $this->assertTrue( true ); // If we get here, no exception was thrown
- }
-
- /**
- * Test WordPress hook registration.
- *
- * @return void
- */
- public function test_wordpress_hook_registration() {
- $block_registrar = new TestBlockRegistrar();
-
- // This should not throw an exception when registering hooks
- $block_registrar->register();
- $this->assertTrue( true ); // If we get here, register() worked without throwing
- }
-
- /**
- * Test that multiple BlockRegistrar instances don't conflict.
- *
- * @return void
- */
- public function test_multiple_instances_no_conflict() {
- // Create two different instances
- $theme_blocks = new TestBlockRegistrar();
- $plugin_blocks = new TestMultiDirectoryBlockRegistrar();
-
- // Test that both instances can be created without conflicts
- $this->assertInstanceOf( \TenupFramework\BlockRegistrar::class, $theme_blocks );
- $this->assertInstanceOf( \TenupFramework\BlockRegistrar::class, $plugin_blocks );
- $this->assertNotSame( $theme_blocks, $plugin_blocks );
- }
-
- /**
- * Test static block name tracking.
- *
- * @return void
- */
- public function test_static_block_name_tracking() {
- // Use reflection to access static properties
- $reflection = new \ReflectionClass( \TenupFramework\BlockRegistrar::class );
-
- // Test that static properties exist
- $this->assertTrue( $reflection->hasProperty( 'registered_block_names' ) );
- $this->assertTrue( $reflection->hasProperty( 'filter_registered' ) );
- $this->assertTrue( $reflection->hasProperty( 'block_sources' ) );
-
- // Test that the static filter method exists
- $this->assertTrue( $reflection->hasMethod( 'filter_allowed_block_types' ) );
- }
-
- /**
- * Test block conflict detection methods.
- *
- * @return void
- */
- public function test_block_conflict_detection() {
- // Test conflict detection methods exist
- $this->assertTrue( method_exists( \TenupFramework\BlockRegistrar::class, 'has_block_conflict' ) );
- $this->assertTrue( method_exists( \TenupFramework\BlockRegistrar::class, 'get_block_source' ) );
- $this->assertTrue( method_exists( \TenupFramework\BlockRegistrar::class, 'get_all_block_sources' ) );
-
- // Test initial state
- $this->assertFalse( \TenupFramework\BlockRegistrar::has_block_conflict( 'test/block' ) );
- $this->assertNull( \TenupFramework\BlockRegistrar::get_block_source( 'test/block' ) );
- $this->assertIsArray( \TenupFramework\BlockRegistrar::get_all_block_sources() );
- }
-
- /**
- * Test block source tracking.
- *
- * @return void
- */
- public function test_block_source_tracking() {
- // Manually add a block source for testing
- \TenupFramework\BlockRegistrar::$block_sources['test/block'] = 'TestClass';
-
- // Test conflict detection
- $this->assertTrue( \TenupFramework\BlockRegistrar::has_block_conflict( 'test/block' ) );
- $this->assertEquals( 'TestClass', \TenupFramework\BlockRegistrar::get_block_source( 'test/block' ) );
-
- // Test getting all sources
- $sources = \TenupFramework\BlockRegistrar::get_all_block_sources();
- $this->assertArrayHasKey( 'test/block', $sources );
- $this->assertEquals( 'TestClass', $sources['test/block'] );
-
- // Clean up
- unset( \TenupFramework\BlockRegistrar::$block_sources['test/block'] );
- }
-
- /**
- * Test path validation edge cases.
- *
- * @return void
- */
- public function test_path_validation_edge_cases() {
- $block_registrar = new TestBlockRegistrar();
-
- // Use reflection to access protected method
- $reflection = new \ReflectionClass( $block_registrar );
- $method = $reflection->getMethod( 'validate_directory_path' );
- $method->setAccessible( true );
-
- // Test invalid paths
- $this->assertFalse( $method->invoke( $block_registrar, '' ) );
- $this->assertFalse( $method->invoke( $block_registrar, '../malicious' ) );
- $this->assertFalse( $method->invoke( $block_registrar, './relative' ) );
- $this->assertFalse( $method->invoke( $block_registrar, str_repeat( 'a', 1001 ) ) );
-
- // Test valid paths
- $this->assertEquals( '/valid/path/', $method->invoke( $block_registrar, '/valid/path' ) );
- $this->assertEquals( '/valid/path/', $method->invoke( $block_registrar, '/valid/path/' ) );
- $this->assertEquals( '/valid/path/', $method->invoke( $block_registrar, '\\valid\\path' ) );
- }
-
- /**
- * Test block.json validation edge cases.
- *
- * @return void
- */
- public function test_block_json_validation_edge_cases() {
- $block_registrar = new TestBlockRegistrar();
-
- // Use reflection to access protected method
- $reflection = new \ReflectionClass( $block_registrar );
- $method = $reflection->getMethod( 'validate_block_json' );
- $method->setAccessible( true );
-
- // Test invalid file paths
- $this->assertFalse( $method->invoke( $block_registrar, '/non/existent/file.json' ) );
-
- // Test invalid JSON (this would require creating actual files in tests)
- // For now, just test that the method exists and handles errors
- $this->assertTrue( method_exists( $block_registrar, 'validate_block_json' ) );
- }
-
- /**
- * Test WordPress availability check.
- *
- * @return void
- */
- public function test_wordpress_availability_check() {
- $block_registrar = new TestBlockRegistrar();
-
- // Test that the method exists and can be called
- // In a real WordPress environment, it would check for function availability
- $this->assertTrue( method_exists( $block_registrar, 'register_blocks' ) );
- }
-}
diff --git a/tests/Cache/ReadOnlyFileDiscoverCacheDriverTest.php b/tests/Cache/ReadOnlyFileDiscoverCacheDriverTest.php
deleted file mode 100644
index 23fd831..0000000
--- a/tests/Cache/ReadOnlyFileDiscoverCacheDriverTest.php
+++ /dev/null
@@ -1,118 +0,0 @@
-dir = sys_get_temp_dir() . '/tenup_ro_driver_' . uniqid( '', true );
- mkdir( $this->dir );
-
- return $this->dir;
- }
-
- /**
- * Remove the temporary directory after a test.
- *
- * @return void
- */
- protected function tearDown(): void { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
- if ( '' !== $this->dir && is_dir( $this->dir ) ) {
- $files = glob( $this->dir . '/*' );
- if ( false !== $files ) {
- array_map( 'unlink', $files );
- }
- rmdir( $this->dir );
- }
- $this->dir = '';
-
- parent::tearDown();
- }
-
- /**
- * The constructor must not create the cache directory: the runtime never writes.
- *
- * @return void
- */
- public function test_constructor_does_not_create_directory() {
- $missing = sys_get_temp_dir() . '/tenup_ro_missing_' . uniqid( '', true );
-
- new ReadOnlyFileDiscoverCacheDriver( $missing, false, 'cache.php' );
-
- $this->assertDirectoryDoesNotExist( $missing );
- }
-
- /**
- * put() is a no-op: nothing is written to disk.
- *
- * @return void
- */
- public function test_put_writes_nothing() {
- $dir = $this->make_dir();
- $driver = new ReadOnlyFileDiscoverCacheDriver( $dir, false, 'cache.php' );
-
- $driver->put( 'id', [ 'Foo\\Bar' ] );
-
- $this->assertFalse( $driver->has( 'id' ) );
- $this->assertFileDoesNotExist( $dir . '/cache.php' );
- }
-
- /**
- * forget() is a no-op: an existing cache file is left untouched.
- *
- * @return void
- */
- public function test_forget_deletes_nothing() {
- $dir = $this->make_dir();
- file_put_contents( $dir . '/cache.php', 'forget( 'id' );
-
- $this->assertFileExists( $dir . '/cache.php' );
- }
-
- /**
- * has()/get() read an existing cache file produced with the same settings the
- * build-time generator uses (serialize = false, an explicit filename).
- *
- * @return void
- */
- public function test_has_and_get_read_an_existing_file() {
- $dir = $this->make_dir();
- file_put_contents( $dir . '/cache.php', "assertTrue( $driver->has( 'id' ) );
- $this->assertSame( [ 'Foo\\Bar' ], $driver->get( 'id' ) );
- }
-}
diff --git a/tests/Core/EmojiTest.php b/tests/Core/EmojiTest.php
deleted file mode 100644
index f9250f1..0000000
--- a/tests/Core/EmojiTest.php
+++ /dev/null
@@ -1,226 +0,0 @@
-assertInstanceOf( \TenupFramework\ModuleInterface::class, $emoji );
- }
-
- /**
- * Test that Emoji can be registered.
- *
- * @return void
- */
- public function test_can_register() {
- $emoji = new Emoji();
-
- $this->assertTrue( $emoji->can_register() );
- }
-
- /**
- * Test that Emoji has correct load order.
- *
- * @return void
- */
- public function test_load_order() {
- $emoji = new Emoji();
-
- $this->assertEquals( 5, $emoji->load_order() );
- }
-
- /**
- * Test that register method can be called without errors.
- *
- * @return void
- */
- public function test_register_can_be_called() {
- $emoji = new Emoji();
-
- // Mock WordPress functions to prevent actual function calls
- \Brain\Monkey\Functions\when( 'remove_action' )->justReturn( true );
- \Brain\Monkey\Functions\when( 'remove_filter' )->justReturn( true );
- \Brain\Monkey\Functions\when( 'add_filter' )->justReturn( true );
-
- // This should not throw any exceptions
- $emoji->register();
-
- // If we get here, the method executed successfully
- $this->assertTrue( true );
- }
-
- /**
- * Test that register method exists and is callable.
- *
- * @return void
- */
- public function test_register_method_exists() {
- $emoji = new Emoji();
-
- $this->assertTrue( method_exists( $emoji, 'register' ) );
- $this->assertTrue( is_callable( [ $emoji, 'register' ] ) );
- }
-
- /**
- * Test that Emoji has the expected WordPress function calls in register method.
- *
- * @return void
- */
- public function test_register_method_contains_expected_calls() {
- $reflection = new \ReflectionClass( Emoji::class );
- $method = $reflection->getMethod( 'register' );
- $filename = $method->getFileName();
- $start_line = $method->getStartLine();
- $end_line = $method->getEndLine();
-
- // Read the method source code
- $lines = file( $filename );
- $method_source = implode( '', array_slice( $lines, $start_line - 1, $end_line - $start_line + 1 ) );
-
- // Verify the method contains the expected remove_action calls
- $this->assertStringContainsString( "remove_action( 'wp_head', 'print_emoji_detection_script', 7 )", $method_source );
- $this->assertStringContainsString( "remove_action( 'admin_print_scripts', 'print_emoji_detection_script' )", $method_source );
- $this->assertStringContainsString( "remove_action( 'wp_print_styles', 'print_emoji_styles' )", $method_source );
- $this->assertStringContainsString( "remove_action( 'admin_print_styles', 'print_emoji_styles' )", $method_source );
-
- // Verify the method contains the expected remove_filter calls
- $this->assertStringContainsString( "remove_filter( 'the_content_feed', 'wp_staticize_emoji' )", $method_source );
- $this->assertStringContainsString( "remove_filter( 'comment_text_rss', 'wp_staticize_emoji' )", $method_source );
- $this->assertStringContainsString( "remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' )", $method_source );
-
- // Verify the method contains the expected add_filter calls
- $this->assertStringContainsString( "add_filter( 'tiny_mce_plugins', [ \$this, 'disable_emojis_tinymce' ] )", $method_source );
- $this->assertStringContainsString( "add_filter( 'wp_resource_hints', [ \$this, 'disable_emoji_dns_prefetch' ], 10, 2 )", $method_source );
- }
-
- /**
- * Test that disable_emojis_tinymce method exists and is callable.
- *
- * @return void
- */
- public function test_disable_emojis_tinymce_method_exists() {
- $emoji = new Emoji();
-
- $this->assertTrue( method_exists( $emoji, 'disable_emojis_tinymce' ) );
- $this->assertTrue( is_callable( [ $emoji, 'disable_emojis_tinymce' ] ) );
- }
-
- /**
- * Test that disable_emoji_dns_prefetch method exists and is callable.
- *
- * @return void
- */
- public function test_disable_emoji_dns_prefetch_method_exists() {
- $emoji = new Emoji();
-
- $this->assertTrue( method_exists( $emoji, 'disable_emoji_dns_prefetch' ) );
- $this->assertTrue( is_callable( [ $emoji, 'disable_emoji_dns_prefetch' ] ) );
- }
-
- /**
- * Test disable_emojis_tinymce method functionality.
- *
- * @return void
- */
- public function test_disable_emojis_tinymce_functionality() {
- $emoji = new Emoji();
-
- // Test with wpemoji plugin present
- $plugins_with_emoji = [ 'wordpress', 'wpemoji', 'media' ];
- $result = $emoji->disable_emojis_tinymce( $plugins_with_emoji );
-
- $this->assertNotContains( 'wpemoji', $result );
- $this->assertContains( 'wordpress', $result );
- $this->assertContains( 'media', $result );
-
- // Test with wpemoji plugin not present
- $plugins_without_emoji = [ 'wordpress', 'media' ];
- $result = $emoji->disable_emojis_tinymce( $plugins_without_emoji );
-
- $this->assertEquals( $plugins_without_emoji, $result );
- }
-
- /**
- * Test disable_emoji_dns_prefetch method functionality.
- *
- * @return void
- */
- public function test_disable_emoji_dns_prefetch_functionality() {
- $emoji = new Emoji();
-
- // Mock apply_filters for emoji_svg_url
- \Brain\Monkey\Filters\expectApplied( 'emoji_svg_url' )
- ->once()
- ->andReturn( 'https://s.w.org/images/core/emoji/2/svg/' );
-
- $urls = [
- 'https://fonts.googleapis.com',
- 'https://s.w.org/images/core/emoji/2/svg/',
- 'https://example.com',
- ];
-
- $result = $emoji->disable_emoji_dns_prefetch( $urls, 'dns-prefetch' );
-
- $this->assertNotContains( 'https://s.w.org/images/core/emoji/2/svg/', $result );
- $this->assertContains( 'https://fonts.googleapis.com', $result );
- $this->assertContains( 'https://example.com', $result );
-
- // Test with different relation type
- $result = $emoji->disable_emoji_dns_prefetch( $urls, 'preconnect' );
- $this->assertEquals( $urls, $result );
- }
-
- /**
- * Test that Emoji can be instantiated multiple times.
- *
- * @return void
- */
- public function test_multiple_instances() {
- $emoji_1 = new Emoji();
- $emoji_2 = new Emoji();
-
- $this->assertInstanceOf( Emoji::class, $emoji_1 );
- $this->assertInstanceOf( Emoji::class, $emoji_2 );
- $this->assertNotSame( $emoji_1, $emoji_2 );
- }
-
- /**
- * Test that Emoji uses the Module trait.
- *
- * @return void
- */
- public function test_uses_module_trait() {
- $emoji = new Emoji();
-
- // Check that the class has the methods from the Module trait
- $this->assertTrue( method_exists( $emoji, 'load_order' ) );
- $this->assertTrue( method_exists( $emoji, 'can_register' ) );
- $this->assertTrue( method_exists( $emoji, 'register' ) );
- }
-}
diff --git a/tests/Core/HeadOverridesTest.php b/tests/Core/HeadOverridesTest.php
deleted file mode 100644
index b15d940..0000000
--- a/tests/Core/HeadOverridesTest.php
+++ /dev/null
@@ -1,137 +0,0 @@
-assertInstanceOf( \TenupFramework\ModuleInterface::class, $head_overrides );
- }
-
- /**
- * Test that HeadOverrides can be registered.
- *
- * @return void
- */
- public function test_can_register() {
- $head_overrides = new HeadOverrides();
-
- $this->assertTrue( $head_overrides->can_register() );
- }
-
- /**
- * Test that HeadOverrides has correct load order.
- *
- * @return void
- */
- public function test_load_order() {
- $head_overrides = new HeadOverrides();
-
- $this->assertEquals( 5, $head_overrides->load_order() );
- }
-
- /**
- * Test that register method can be called without errors.
- *
- * @return void
- */
- public function test_register_can_be_called() {
- $head_overrides = new HeadOverrides();
-
- // Mock remove_action to prevent actual WordPress function calls
- \Brain\Monkey\Functions\when( 'remove_action' )->justReturn( true );
-
- // This should not throw any exceptions
- $head_overrides->register();
-
- // If we get here, the method executed successfully
- $this->assertTrue( true );
- }
-
- /**
- * Test that register method exists and is callable.
- *
- * @return void
- */
- public function test_register_method_exists() {
- $head_overrides = new HeadOverrides();
-
- $this->assertTrue( method_exists( $head_overrides, 'register' ) );
- $this->assertTrue( is_callable( [ $head_overrides, 'register' ] ) );
- }
-
- /**
- * Test that HeadOverrides has the expected WordPress function calls in register method.
- *
- * @return void
- */
- public function test_register_method_contains_expected_calls() {
- $reflection = new \ReflectionClass( HeadOverrides::class );
- $method = $reflection->getMethod( 'register' );
- $filename = $method->getFileName();
- $start_line = $method->getStartLine();
- $end_line = $method->getEndLine();
-
- // Read the method source code
- $lines = file( $filename );
- $method_source = implode( '', array_slice( $lines, $start_line - 1, $end_line - $start_line + 1 ) );
-
- // Verify the method contains the expected remove_action calls
- $this->assertStringContainsString( "remove_action( 'wp_head', 'wp_generator' )", $method_source );
- $this->assertStringContainsString( "remove_action( 'wp_head', 'wlwmanifest_link' )", $method_source );
- $this->assertStringContainsString( "remove_action( 'wp_head', 'rsd_link' )", $method_source );
- }
-
- /**
- * Test that HeadOverrides can be instantiated multiple times.
- *
- * @return void
- */
- public function test_multiple_instances() {
- $head_overrides_1 = new HeadOverrides();
- $head_overrides_2 = new HeadOverrides();
-
- $this->assertInstanceOf( HeadOverrides::class, $head_overrides_1 );
- $this->assertInstanceOf( HeadOverrides::class, $head_overrides_2 );
- $this->assertNotSame( $head_overrides_1, $head_overrides_2 );
- }
-
- /**
- * Test that HeadOverrides uses the Module trait.
- *
- * @return void
- */
- public function test_uses_module_trait() {
- $head_overrides = new HeadOverrides();
-
- // Check that the class has the methods from the Module trait
- $this->assertTrue( method_exists( $head_overrides, 'load_order' ) );
- $this->assertTrue( method_exists( $head_overrides, 'can_register' ) );
- $this->assertTrue( method_exists( $head_overrides, 'register' ) );
- }
-}
diff --git a/tests/Debug/LoaderDebugTest.php b/tests/Debug/LoaderDebugTest.php
deleted file mode 100644
index 6eab4fd..0000000
--- a/tests/Debug/LoaderDebugTest.php
+++ /dev/null
@@ -1,522 +0,0 @@
-
- */
- private function sample_record( string $directory = '/srv/site/wp-content/plugins/demo/inc' ): array {
- return [
- 'directory' => $directory,
- 'cache_file' => $directory . '/class-loader-cache/class-loader-cache-v2.php',
- 'cache_exists' => false,
- 'cache_used' => false,
- 'cache_disabled' => false,
- 'classes' => [ 'TenupTmp\\Widget' ],
- 'version' => '1.3.0',
- 'reference' => 'abcdef1234567890',
- 'discovery_seconds' => 0.0123,
- 'lookup_seconds' => 0.0456,
- ];
- }
-
- /**
- * Stub the functions record() needs, with the tooling enabled.
- *
- * @return void
- */
- private function stub_enabled() {
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
- }
-
- /**
- * is_enabled() is false when the enable filter returns false.
- *
- * @return void
- */
- public function test_is_enabled_false_when_filter_disables() {
- when( 'add_action' )->justReturn( true );
- when( 'apply_filters' )->justReturn( false );
-
- $this->assertFalse( LoaderDebug::is_enabled() );
- }
-
- /**
- * is_enabled() is false when the disable constant is set.
- *
- * @runInSeparateProcess
- * @preserveGlobalState disabled
- *
- * @return void
- */
- public function test_is_enabled_false_when_constant_defined() {
- when( 'add_action' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
-
- define( 'TENUP_FRAMEWORK_DISABLE_LOADER_DEBUG', true );
-
- $this->assertFalse( LoaderDebug::is_enabled() );
- }
-
- /**
- * record() stores the record when enabled.
- *
- * @return void
- */
- public function test_record_stores_when_enabled() {
- $this->stub_enabled();
-
- LoaderDebug::record( $this->sample_record() );
-
- $this->assertCount( 1, LoaderDebug::get_loaders() );
- }
-
- /**
- * record() accumulates multiple records.
- *
- * @return void
- */
- public function test_records_accumulate() {
- $this->stub_enabled();
-
- LoaderDebug::record( $this->sample_record( '/a/inc' ) );
- LoaderDebug::record( $this->sample_record( '/b/inc' ) );
-
- $this->assertCount( 2, LoaderDebug::get_loaders() );
- }
-
- /**
- * record() stores nothing when disabled.
- *
- * @return void
- */
- public function test_record_skips_when_disabled() {
- when( 'add_action' )->justReturn( true );
- when( 'apply_filters' )->justReturn( false );
-
- LoaderDebug::record( $this->sample_record() );
-
- $this->assertSame( [], LoaderDebug::get_loaders() );
- }
-
- /**
- * The callback registered on the aggregation filter merges this copy's records into
- * whatever other copies have already contributed.
- *
- * @return void
- */
- public function test_aggregation_filter_merges_records() {
- $captured = null;
-
- when( 'add_action' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
- when( 'add_filter' )->alias(
- static function ( $hook, $callback ) use ( &$captured ) {
- if ( LoaderDebug::FILTER === $hook ) {
- $captured = $callback;
- }
- return true;
- }
- );
-
- LoaderDebug::record( $this->sample_record( '/b/inc' ) );
-
- $this->assertIsCallable( $captured );
-
- // A record contributed by another copy should be preserved alongside ours.
- $existing = [ [ 'directory' => '/a/inc' ] ];
- $merged = $captured( $existing );
-
- $this->assertCount( 2, $merged );
- $this->assertSame( '/a/inc', $merged[0]['directory'] );
- $this->assertSame( '/b/inc', $merged[1]['directory'] );
- }
-
- /**
- * render_page() lists each loader and the classes it loaded.
- *
- * @return void
- */
- public function test_render_page_lists_loaders_and_classes() {
- $this->stub_render_environment();
-
- LoaderDebug::record( $this->sample_record() );
-
- $output = $this->capture_render();
-
- $this->assertStringContainsString( 'WP Framework Loaders', $output );
- $this->assertStringContainsString( '/srv/site/wp-content/plugins/demo/inc', $output );
- $this->assertStringContainsString( 'TenupTmp\\Widget', $output );
- $this->assertStringContainsString( 'Check this cache for staleness', $output );
-
- // The recorded discovery/lookup timings are surfaced on the page.
- $this->assertStringContainsString( 'Discovery time', $output );
- $this->assertStringContainsString( 'Class lookup time', $output );
- $this->assertStringContainsString( '12.30 ms', $output ); // 0.0123s discovery.
- $this->assertStringContainsString( '45.60 ms', $output ); // 0.0456s lookup.
- }
-
- /**
- * owner_label() derives a plugin name when the directory sits under the plugins root.
- *
- * @runInSeparateProcess
- * @preserveGlobalState disabled
- *
- * @return void
- */
- public function test_owner_label_derives_plugin_name() {
- define( 'WP_PLUGIN_DIR', '/srv/site/wp-content/plugins' );
-
- $method = ( new \ReflectionClass( LoaderDebug::class ) )->getMethod( 'owner_label' );
- $method->setAccessible( true );
-
- $this->assertSame(
- 'Plugin: demo',
- $method->invoke( null, '/srv/site/wp-content/plugins/demo/inc' )
- );
- }
-
- /**
- * render_page() reports drift when a staleness check is requested with a valid nonce.
- *
- * @return void
- */
- public function test_render_page_reports_staleness_drift() {
- $this->stub_render_environment();
- when( 'wp_verify_nonce' )->justReturn( true );
-
- $dir = $this->make_temp_class_dir();
-
- // The loaded list (in the record) is deliberately out of date versus what is on disk.
- $record = $this->sample_record( $dir );
- $record['classes'] = [ 'TenupTmp\\Old' ];
- LoaderDebug::record( $record );
-
- $_GET['check'] = md5( $dir );
- $_GET['_wpnonce'] = 'test';
-
- $output = $this->capture_render();
-
- unset( $_GET['check'], $_GET['_wpnonce'] );
- $this->remove_temp_dir( $dir );
-
- $this->assertStringContainsString( 'Stale', $output );
- $this->assertStringContainsString( 'TenupTmp\\Widget', $output ); // On disk, missing from cache.
- $this->assertStringContainsString( 'TenupTmp\\Old', $output ); // In cache, gone from disk.
- // The drift notice also reports a real, positive live-discovery duration.
- $this->assertMatchesRegularExpression( '/Live discovery took \d[\d.,]* (ms|s)\./', $output );
- }
-
- /**
- * render_page() confirms an up-to-date cache and reports how long the live scan took when a
- * staleness check is requested and the loaded list matches disk.
- *
- * @return void
- */
- public function test_render_page_reports_up_to_date_and_timing() {
- $this->stub_render_environment();
- when( 'wp_verify_nonce' )->justReturn( true );
-
- $dir = $this->make_temp_class_dir();
-
- // The loaded list matches what is actually on disk (the single Widget class).
- $record = $this->sample_record( $dir );
- $record['classes'] = [ 'TenupTmp\\Widget' ];
- LoaderDebug::record( $record );
-
- $_GET['check'] = md5( $dir );
- $_GET['_wpnonce'] = 'test';
-
- $output = $this->capture_render();
-
- unset( $_GET['check'], $_GET['_wpnonce'] );
- $this->remove_temp_dir( $dir );
-
- $this->assertStringContainsString( 'Up to date', $output );
- // Require a real, positive duration — this must NOT match the "Live discovery took —."
- // placeholder that format_duration() emits for a non-positive/absent value.
- $this->assertMatchesRegularExpression( '/Live discovery took \d[\d.,]* (ms|s)\./', $output );
- }
-
- /**
- * cache_state() maps each combination of the record flags to the expected severity and badge.
- *
- * @dataProvider cache_state_provider
- *
- * @param array $flags The cache_* flags to set on the record.
- * @param string $expected_sev The expected severity.
- * @param string $expected_snippet A substring expected in the badge.
- *
- * @return void
- */
- public function test_cache_state_resolves_expected_states( array $flags, string $expected_sev, string $expected_snippet ) {
- $state = $this->invoke_protected( 'cache_state', [ array_merge( $this->sample_record(), $flags ) ] );
-
- $this->assertSame( $expected_sev, $state['severity'] );
- $this->assertStringContainsString( $expected_snippet, $state['badge'] );
- }
-
- /**
- * Data for test_cache_state_resolves_expected_states.
- *
- * @return array, 1: string, 2: string}>
- */
- public function cache_state_provider(): array {
- return [
- 'disabled' => [ [ 'cache_disabled' => true ], 'warn', 'disabled' ],
- 'uncached' => [
- [
- 'cache_exists' => false,
- 'cache_used' => false,
- ],
- 'warn',
- 'Uncached',
- ],
- 'present but unused' => [
- [
- 'cache_exists' => true,
- 'cache_used' => false,
- ],
- 'error',
- 'not used',
- ],
- 'failed to load' => [
- [
- 'cache_exists' => true,
- 'cache_used' => false,
- 'cache_failed' => true,
- ],
- 'error',
- 'failed to load',
- ],
- 'in use' => [
- [
- 'cache_exists' => true,
- 'cache_used' => true,
- ],
- 'ok',
- 'in use',
- ],
- ];
- }
-
- /**
- * legacy_files() reports files in the cache directory that are not the current cache file,
- * and nothing when the directory is clean or absent.
- *
- * @return void
- */
- public function test_legacy_files_detects_unexpected_files() {
- $dir = $this->make_temp_class_dir();
- $cache_dir = $dir . '/class-loader-cache';
- mkdir( $cache_dir );
-
- $current = $cache_dir . '/class-loader-cache-v2.php';
- file_put_contents( $current, 'assertSame( [], $this->invoke_protected( 'legacy_files', [ $current ] ) );
-
- // A leftover file from an older version is reported.
- file_put_contents( $cache_dir . '/discoverer-cache-TenupFramework', 'x' );
- $found = $this->invoke_protected( 'legacy_files', [ $current ] );
- $this->assertContains( 'discoverer-cache-TenupFramework', $found );
- $this->assertNotContains( 'class-loader-cache-v2.php', $found );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * legacy_files() is empty when the cache directory does not exist.
- *
- * @return void
- */
- public function test_legacy_files_empty_when_directory_absent() {
- $missing = sys_get_temp_dir() . '/tenup_missing_' . uniqid( '', true ) . '/class-loader-cache-v2.php';
-
- $this->assertSame( [], $this->invoke_protected( 'legacy_files', [ $missing ] ) );
- }
-
- /**
- * format_duration() picks a sensible unit and renders a placeholder for non-positive input.
- *
- * @dataProvider duration_provider
- *
- * @param mixed $seconds The duration in seconds.
- * @param string $expected The expected rendered string.
- *
- * @return void
- */
- public function test_format_duration( $seconds, string $expected ) {
- $this->assertSame( $expected, $this->invoke_protected( 'format_duration', [ $seconds ] ) );
- }
-
- /**
- * Data for test_format_duration.
- *
- * @return array
- */
- public function duration_provider(): array {
- return [
- 'zero' => [ 0.0, '—' ],
- 'negative' => [ -0.005, '—' ],
- 'non-numeric' => [ 'nope', '—' ],
- 'not-a-number' => [ NAN, '—' ],
- 'infinite' => [ INF, '—' ],
- 'sub-milli' => [ 0.0004, '0.400 ms' ],
- 'milliseconds' => [ 0.0123, '12.30 ms' ],
- 'seconds' => [ 1.5, '1.50 s' ],
- ];
- }
-
- /**
- * cache_detail() renders "Built ago · · " with the build time in UTC.
- *
- * @return void
- */
- public function test_cache_detail_shows_size_and_utc_build_time() {
- when( 'human_time_diff' )->justReturn( '5 minutes' );
- when( 'size_format' )->alias( static fn( $bytes ) => $bytes . ' B' );
-
- $dir = $this->make_temp_class_dir();
- $cache_dir = $dir . '/class-loader-cache';
- mkdir( $cache_dir );
- $cache_file = $cache_dir . '/class-loader-cache-v2.php';
- file_put_contents( $cache_file, 'invoke_protected( 'cache_detail', [ [ 'cache_file' => $cache_file ] ] );
-
- $this->assertStringContainsString( 'Built 5 minutes ago', $detail );
- // The absolute build time is the file mtime rendered in UTC as the trailing segment.
- $expected_utc = gmdate( 'Y-m-d H:i:s', (int) filemtime( $cache_file ) ) . ' UTC';
- $this->assertStringContainsString( '· ' . $expected_utc, $detail );
- $this->assertMatchesRegularExpression( '/·\s*\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$/', $detail );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * Invoke a protected static method on LoaderDebug via reflection.
- *
- * @param string $method The method name.
- * @param array $args The arguments.
- *
- * @return mixed
- */
- private function invoke_protected( string $method, array $args ) {
- $reflection = ( new \ReflectionClass( LoaderDebug::class ) )->getMethod( $method );
- $reflection->setAccessible( true );
-
- return $reflection->invokeArgs( null, $args );
- }
-
- /**
- * Stub everything render_page() touches, with the tooling enabled and the current user
- * capable. apply_filters returns this copy's records for the aggregation filter.
- *
- * @return void
- */
- private function stub_render_environment() {
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'current_user_can' )->justReturn( true );
- when( 'sanitize_text_field' )->returnArg( 1 );
- when( 'wp_unslash' )->returnArg( 1 );
- when( 'admin_url' )->alias(
- static function ( $path = '' ) {
- return 'http://example.test/wp-admin/' . $path;
- }
- );
- when( 'add_query_arg' )->alias(
- static function ( $args, $url ) {
- return $url . '?' . http_build_query( (array) $args );
- }
- );
- when( 'wp_nonce_url' )->returnArg( 1 );
- when( 'apply_filters' )->alias(
- static function ( $hook, $value = null ) {
- if ( LoaderDebug::FILTER === $hook ) {
- return LoaderDebug::get_loaders();
- }
- return $value;
- }
- );
- }
-
- /**
- * Capture the output of render_page().
- *
- * @return string
- */
- private function capture_render(): string {
- ob_start();
- LoaderDebug::render_page();
- return (string) ob_get_clean();
- }
-
- /**
- * Create a temporary directory containing a single discoverable class.
- *
- * @return string The created directory path.
- */
- private function make_temp_class_dir(): string {
- $dir = sys_get_temp_dir() . '/tenup_loader_debug_' . uniqid( '', true );
- mkdir( $dir );
- file_put_contents( $dir . '/Widget.php', "isDir() ) {
- rmdir( $item->getPathname() );
- } else {
- unlink( $item->getPathname() );
- }
- }
-
- rmdir( $dir );
- }
-}
diff --git a/tests/Doubles/ConfigurableBlockRegistrar.php b/tests/Doubles/ConfigurableBlockRegistrar.php
new file mode 100644
index 0000000..8dac7b3
--- /dev/null
+++ b/tests/Doubles/ConfigurableBlockRegistrar.php
@@ -0,0 +1,46 @@
+
+ */
+ public array $directories;
+
+ /**
+ * Constructor.
+ *
+ * @param array $directories The block directories to scan.
+ */
+ public function __construct( array $directories = [] ) {
+ $this->directories = $directories;
+ }
+
+ /**
+ * Get the blocks directory paths.
+ *
+ * @return array
+ */
+ public function get_blocks_directory(): array {
+ return $this->directories;
+ }
+}
diff --git a/tests/Doubles/SecondBlockRegistrar.php b/tests/Doubles/SecondBlockRegistrar.php
new file mode 100644
index 0000000..e6943b0
--- /dev/null
+++ b/tests/Doubles/SecondBlockRegistrar.php
@@ -0,0 +1,20 @@
+
- */
- public static $registered_taxonomies = [];
-
- /**
- * Registered post types.
- *
- * @var array
- */
- public static $registered_post_types = [];
-
- /**
- * Set up the test.
- *
- * @return void
- */
- protected function setUp(): void { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
- parent::setUp();
- Monkey\setUp();
-
- stubs(
- [
- 'wp_get_environment_type' => 'local',
- // Default to the front end so existing tests don't trigger admin-only debug
- // recording; admin tests override this with their own stub.
- 'is_admin' => false,
- 'sanitize_title' => function ( $title ) {
- return str_replace( ' ', '-', strtolower( $title ) );
- },
- 'register_post_type' => function ( $slug, $args ) {
- self::$registered_post_types[ $slug ] = $args;
- },
- 'register_taxonomy_for_object_type' => '__return_true',
- 'register_taxonomy' => function ( $slug, $object_type, $args ) {
- self::$registered_taxonomies[ $slug ] = $args;
- },
- ]
- );
-
- stubEscapeFunctions();
- stubTranslationFunctions();
-
- $this->reset_module_initialization();
- $this->reset_loader_debug();
- }
-
- /**
- * Reset the ModuleInitialization singleton so its accumulated `$classes` do not leak between
- * tests. The suite is not process-isolated (the trait-level annotation does not take effect),
- * so without this a class registered in one test would be seen as "already initialized" in a
- * later one.
- *
- * @return void
- */
- protected function reset_module_initialization(): void {
- if ( ! class_exists( \TenupFramework\ModuleInitialization::class ) ) {
- return;
- }
-
- $instance = ( new \ReflectionClass( \TenupFramework\ModuleInitialization::class ) )->getProperty( 'instance' );
- $instance->setAccessible( true );
- $instance->setValue( null, null );
- }
-
- /**
- * Reset the static state of the LoaderDebug registry so each test starts clean,
- * independent of test execution order or process isolation.
- *
- * @return void
- */
- protected function reset_loader_debug(): void {
- if ( ! class_exists( \TenupFramework\Debug\LoaderDebug::class ) ) {
- return;
- }
-
- $reflection = new \ReflectionClass( \TenupFramework\Debug\LoaderDebug::class );
-
- $loaders = $reflection->getProperty( 'loaders' );
- $loaders->setAccessible( true );
- $loaders->setValue( null, [] );
-
- $booted = $reflection->getProperty( 'booted' );
- $booted->setAccessible( true );
- $booted->setValue( null, false );
-
- unset( $GLOBALS['tenup_framework_debug_page_registered'] );
- }
-
- /**
- * Tear down the test.
- *
- * @return void
- */
- protected function tearDown(): void { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
- self::$registered_taxonomies = [];
- self::$registered_post_types = [];
-
- Monkey\tearDown();
- parent::tearDown();
- }
-}
diff --git a/tests/Helpers.php b/tests/Helpers.php
new file mode 100644
index 0000000..90d7f48
--- /dev/null
+++ b/tests/Helpers.php
@@ -0,0 +1,363 @@
+getSubPathname();
+
+ if ( $item->isDir() ) {
+ mkdir( $destination, 0777, true );
+ continue;
+ }
+
+ copy( $item->getPathname(), $destination );
+ }
+}
+
+/**
+ * Recursively remove a directory.
+ *
+ * @param string $dir The directory to remove.
+ *
+ * @return void
+ */
+function tenup_remove_dir( string $dir ): void {
+ if ( ! is_dir( $dir ) ) {
+ return;
+ }
+
+ $items = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
+
+ foreach ( $items as $item ) {
+ if ( $item->isDir() ) {
+ rmdir( $item->getPathname() );
+ continue;
+ }
+
+ unlink( $item->getPathname() );
+ }
+
+ rmdir( $dir );
+}
+
+/**
+ * Copy one of the committed example directories to a fresh temporary directory.
+ *
+ * @param string $name The example directory name under tests/examples.
+ *
+ * @return string The path to the temporary copy.
+ */
+function tenup_example_copy( string $name ): string {
+ $target = sys_get_temp_dir() . '/tenup_example_' . $name . '_' . uniqid( '', true );
+
+ tenup_copy_dir( dirname( __DIR__ ) . '/tests/examples/' . $name, $target );
+
+ return $target;
+}
+
+/**
+ * The absolute path to the class cache file for a discovery directory.
+ *
+ * @param string $dir The discovery directory.
+ *
+ * @return string
+ */
+function tenup_cache_file_path( string $dir ): string {
+ return $dir . '/' . TenupFramework\ModuleInitialization::CACHE_DIR_NAME
+ . '/' . TenupFramework\ModuleInitialization::CACHE_FILENAME;
+}
+
+/**
+ * Create a temporary directory containing a single discoverable class.
+ *
+ * @return string The created directory path.
+ */
+function tenup_temp_class_dir(): string {
+ $dir = sys_get_temp_dir() . '/tenup_framework_test_' . uniqid( '', true );
+
+ mkdir( $dir );
+ file_put_contents( $dir . '/Widget.php', "setValue( null, null );
+
+ foreach ( [
+ 'loaders' => [],
+ 'booted' => false,
+ ] as $name => $value ) {
+ $property = new ReflectionProperty( TenupFramework\Debug\LoaderDebug::class, $name );
+ $property->setValue( null, $value );
+ }
+
+ // LoaderDebug registers its admin page once per request via this global.
+ unset( $GLOBALS['tenup_framework_debug_page_registered'] );
+}
+
+/**
+ * Reset BlockRegistrar's static registries between tests.
+ *
+ * They are process globals shared by every registrar instance, so without this a block
+ * registered in one test looks like a conflict in the next.
+ *
+ * @return void
+ */
+function tenup_reset_block_registrar(): void {
+ TenupFramework\BlockRegistrar::$registered_block_names = [];
+ TenupFramework\BlockRegistrar::$block_sources = [];
+ TenupFramework\BlockRegistrar::$filter_registered = false;
+}
+
+/**
+ * The registry of temporary directories awaiting cleanup.
+ *
+ * A holder object rather than $this, because Pest's `test()` proxy cannot have array elements
+ * appended to a property.
+ *
+ * @return object{dirs: array}
+ */
+function tenup_temp_dir_registry(): object {
+ static $registry = null;
+
+ if ( null === $registry ) {
+ $registry = new class() {
+ /**
+ * Tracked directories.
+ *
+ * @var array
+ */
+ public array $dirs = [];
+ };
+ }
+
+ return $registry;
+}
+
+/**
+ * Remove every tracked temporary directory and empty the registry.
+ *
+ * @return void
+ */
+function tenup_cleanup_temp_dirs(): void {
+ $registry = tenup_temp_dir_registry();
+
+ foreach ( $registry->dirs as $dir ) {
+ tenup_remove_dir( $dir );
+ }
+
+ $registry->dirs = [];
+}
+
+/**
+ * Build a temporary blocks directory containing real block.json files.
+ *
+ * The directory is tracked for removal by tenup_cleanup_temp_dirs().
+ *
+ * @param array $blocks Keyed by block name
+ * (namespace/name). `json` overrides the generated block.json; `markup` writes a
+ * markup.php alongside it.
+ *
+ * @return string The directory path, with a trailing slash (register_blocks() globs "*\/block.json").
+ */
+function tenup_make_blocks_dir( array $blocks ): string {
+ $root = sys_get_temp_dir() . '/tenup_blocks_' . uniqid( '', true ) . '/';
+ mkdir( $root, 0777, true );
+
+ tenup_temp_dir_registry()->dirs[] = $root;
+
+ foreach ( $blocks as $name => $options ) {
+ $folder = $root . basename( $name );
+ mkdir( $folder );
+
+ $json = $options['json'] ?? (string) json_encode(
+ [
+ 'apiVersion' => 3,
+ 'name' => $name,
+ 'title' => ucfirst( basename( $name ) ),
+ 'category' => 'widgets',
+ ]
+ );
+
+ file_put_contents( $folder . '/block.json', $json );
+
+ if ( isset( $options['markup'] ) ) {
+ file_put_contents( $folder . '/markup.php', $options['markup'] );
+ }
+ }
+
+ return $root;
+}
+
+/**
+ * Invoke a protected static method on LoaderDebug.
+ *
+ * Its rendering helpers are deliberately not public API, but they carry the formatting logic
+ * worth asserting directly rather than only through page output.
+ *
+ * @param string $method The method name.
+ * @param array $args Arguments to pass.
+ *
+ * @return mixed
+ */
+function tenup_invoke_loader_debug( string $method, array $args = [] ): mixed {
+ return ( new ReflectionMethod( TenupFramework\Debug\LoaderDebug::class, $method ) )
+ ->invoke( null, ...$args );
+}
+
+/**
+ * A representative loader record, as ModuleInitialization would hand to LoaderDebug::record().
+ *
+ * @param string $directory The loader directory.
+ *
+ * @return array
+ */
+function tenup_sample_loader_record( string $directory = '/srv/site/wp-content/plugins/demo/inc' ): array {
+ return [
+ 'directory' => $directory,
+ 'cache_file' => $directory . '/class-loader-cache/class-loader-cache-v2.php',
+ 'cache_exists' => false,
+ 'cache_used' => false,
+ 'cache_disabled' => false,
+ 'cache_failed' => false,
+ 'classes' => [ 'TenupTmp\\Widget' ],
+ 'version' => '1.3.0',
+ 'reference' => 'abcdef1234567890',
+ 'discovery_seconds' => 0.0123,
+ 'lookup_seconds' => 0.0456,
+ ];
+}
+
+/**
+ * Create an administrator and switch to them.
+ *
+ * @throws RuntimeException If the user could not be created.
+ *
+ * @return int The new user ID.
+ */
+function tenup_acting_as_admin(): int {
+ $user_id = wp_insert_user(
+ [
+ 'user_login' => 'tenup_admin_' . uniqid( '', false ),
+ 'user_pass' => wp_generate_password(),
+ 'role' => 'administrator',
+ ]
+ );
+
+ if ( is_wp_error( $user_id ) ) {
+ throw new RuntimeException( 'Could not create an administrator: ' . esc_html( $user_id->get_error_message() ) );
+ }
+
+ wp_set_current_user( $user_id );
+
+ return $user_id;
+}
+
+/**
+ * Run a command, returning its stdout, stderr and exit code.
+ *
+ * @param array $command The command and its arguments, unescaped.
+ *
+ * @throws RuntimeException If the process could not be started.
+ *
+ * @return array{stdout: string, stderr: string, exit: int}
+ */
+function tenup_run_process( array $command ): array {
+ $descriptors = [
+ 1 => [ 'pipe', 'w' ],
+ 2 => [ 'pipe', 'w' ],
+ ];
+
+ $process = proc_open(
+ implode( ' ', array_map( 'escapeshellarg', $command ) ),
+ $descriptors,
+ $pipes
+ );
+
+ if ( ! is_resource( $process ) ) {
+ throw new RuntimeException( 'Could not start process: ' . esc_html( implode( ' ', $command ) ) );
+ }
+
+ $stdout = (string) stream_get_contents( $pipes[1] );
+ $stderr = (string) stream_get_contents( $pipes[2] );
+
+ fclose( $pipes[1] );
+ fclose( $pipes[2] );
+
+ return [
+ 'stdout' => $stdout,
+ 'stderr' => $stderr,
+ 'exit' => proc_close( $process ),
+ ];
+}
+
+/**
+ * Run the class-cache bin script, returning its stdout, stderr and exit code.
+ *
+ * Shelling out (rather than calling ModuleInitialization directly) is deliberate: it covers the
+ * script's own autoloader resolution, argument handling and exit codes, the way CI invokes it.
+ *
+ * @param array $args The arguments to pass after the script name.
+ *
+ * @return array{stdout: string, stderr: string, exit: int}
+ */
+function tenup_run_cache_bin( array $args ): array {
+ $script = dirname( __DIR__ ) . '/bin/tenup-framework-generate-class-cache';
+
+ return tenup_run_process( array_merge( [ PHP_BINARY, $script ], $args ) );
+}
+
+/**
+ * Run one of the tests/scripts/ helper scripts in a fresh PHP process.
+ *
+ * @param string $script The filename under tests/scripts/.
+ * @param array $args Arguments to pass to the script.
+ *
+ * @return array{stdout: string, stderr: string, exit: int}
+ */
+function tenup_run_script( string $script, array $args = [] ): array {
+ return tenup_run_process(
+ array_merge( [ PHP_BINARY, __DIR__ . '/scripts/' . $script ], $args )
+ );
+}
diff --git a/tests/Integration/BlockRegistrarTest.php b/tests/Integration/BlockRegistrarTest.php
new file mode 100644
index 0000000..1ef7edc
--- /dev/null
+++ b/tests/Integration/BlockRegistrarTest.php
@@ -0,0 +1,307 @@
+get_all_registered() ) as $name ) {
+ if ( str_starts_with( (string) $name, 'tenup-test/' ) ) {
+ unregister_block_type( (string) $name );
+ }
+ }
+
+ tenup_cleanup_temp_dirs();
+ tenup_reset_block_registrar();
+ }
+);
+
+it(
+ 'is a module the loader will pick up',
+ function (): void {
+ $registrar = new ConfigurableBlockRegistrar();
+
+ expect( $registrar )->toBeInstanceOf( ModuleInterface::class );
+ expect( $registrar->can_register() )->toBeTrue();
+ }
+);
+
+it(
+ 'defers block registration to the init action',
+ function (): void {
+ $registrar = new ConfigurableBlockRegistrar();
+
+ $registrar->register();
+
+ expect( has_action( 'init', [ $registrar, 'register_blocks' ] ) )->toBe( 10 );
+ }
+);
+
+it(
+ 'registers a block from a directory containing block.json',
+ function (): void {
+ $dir = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ $block = WP_Block_Type_Registry::get_instance()->get_registered( 'tenup-test/alpha' );
+
+ expect( $block )->not->toBeNull();
+ expect( $block->title )->toBe( 'Alpha' );
+ }
+);
+
+it(
+ 'registers blocks found across several directories',
+ function (): void {
+ $first = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+ $second = tenup_make_blocks_dir( [ 'tenup-test/beta' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $first, $second ] ) )->register_blocks();
+
+ expect( WP_Block_Type_Registry::get_instance()->is_registered( 'tenup-test/alpha' ) )->toBeTrue();
+ expect( WP_Block_Type_Registry::get_instance()->is_registered( 'tenup-test/beta' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'registers nothing when given no directories',
+ function (): void {
+ ( new ConfigurableBlockRegistrar( [] ) )->register_blocks();
+
+ expect( BlockRegistrar::$registered_block_names )->toBe( [] );
+ }
+);
+
+it(
+ 'skips directories that do not exist',
+ function (): void {
+ $missing = sys_get_temp_dir() . '/tenup_blocks_missing_' . uniqid( '', true ) . '/';
+ $real = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $missing, $real ] ) )->register_blocks();
+
+ // The missing directory is passed first, to prove it does not abort the run.
+ expect( WP_Block_Type_Registry::get_instance()->is_registered( 'tenup-test/alpha' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'skips a block whose block.json is malformed',
+ function (): void {
+ $dir = tenup_make_blocks_dir(
+ [
+ 'tenup-test/broken' => [ 'json' => '{ not valid json' ],
+ 'tenup-test/alpha' => [],
+ ]
+ );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect( WP_Block_Type_Registry::get_instance()->is_registered( 'tenup-test/broken' ) )->toBeFalse();
+ // The valid sibling still registers.
+ expect( WP_Block_Type_Registry::get_instance()->is_registered( 'tenup-test/alpha' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'skips a block.json with no name',
+ function (): void {
+ $dir = tenup_make_blocks_dir(
+ [ 'tenup-test/nameless' => [ 'json' => '{"apiVersion":3,"title":"Nameless"}' ] ]
+ );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect( BlockRegistrar::$registered_block_names )->toBe( [] );
+ }
+);
+
+describe(
+ 'render callbacks',
+ function (): void {
+ it(
+ 'adds no render callback when the block has no markup.php',
+ function (): void {
+ $dir = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+ $registrar = new ConfigurableBlockRegistrar( [ $dir ] );
+
+ $options = ( new ReflectionMethod( $registrar, 'get_block_options' ) )
+ ->invoke( $registrar, $dir . 'alpha' );
+
+ expect( $options )->toBe( [] );
+ }
+ );
+
+ it(
+ 'adds a render callback when the block has markup.php',
+ function (): void {
+ $dir = tenup_make_blocks_dir(
+ [ 'tenup-test/alpha' => [ 'markup' => '' ] ]
+ );
+
+ $registrar = new ConfigurableBlockRegistrar( [ $dir ] );
+
+ $options = ( new ReflectionMethod( $registrar, 'get_block_options' ) )
+ ->invoke( $registrar, $dir . 'alpha' );
+
+ expect( $options )->toHaveKey( 'render_callback' );
+ expect( $options['render_callback'] )->toBeCallable();
+ }
+ );
+
+ it(
+ 'renders the block through markup.php',
+ function (): void {
+ $dir = tenup_make_blocks_dir(
+ [ 'tenup-test/alpha' => [ 'markup' => '' ] ]
+ );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect(
+ render_block(
+ [
+ 'blockName' => 'tenup-test/alpha',
+ 'attrs' => [],
+ 'innerBlocks' => [],
+ 'innerHTML' => '',
+ 'innerContent' => [],
+ ]
+ )
+ )->toContain( 'hello from markup' );
+ }
+ );
+ }
+);
+
+describe(
+ 'source tracking and conflicts',
+ function (): void {
+ it(
+ 'records which class registered each block',
+ function (): void {
+ $dir = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect( BlockRegistrar::get_block_source( 'tenup-test/alpha' ) )
+ ->toBe( ConfigurableBlockRegistrar::class );
+ expect( BlockRegistrar::get_all_block_sources() )->toHaveKey( 'tenup-test/alpha' );
+ }
+ );
+
+ it(
+ 'returns null for a block it never registered',
+ function (): void {
+ expect( BlockRegistrar::get_block_source( 'tenup-test/nope' ) )->toBeNull();
+ }
+ );
+
+ it(
+ 'detects a conflict once a block name is taken',
+ function (): void {
+ $dir = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ expect( BlockRegistrar::has_block_conflict( 'tenup-test/alpha' ) )->toBeFalse();
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect( BlockRegistrar::has_block_conflict( 'tenup-test/alpha' ) )->toBeTrue();
+ }
+ );
+
+ it(
+ 'keeps the original source when a second registrar claims the same name',
+ function (): void {
+ /*
+ * WordPress rejects the duplicate itself, via _doing_it_wrong from
+ * WP_Block_Type_Registry::register. Mantle escalates those to failures unless
+ * declared, so this states that the notice is the expected outcome.
+ *
+ * Worth noting: register_blocks() calls register_block_type_from_metadata() *before*
+ * checking self::$block_sources, and bails on a falsy return. So for two registrars
+ * competing in one request, WordPress's own rejection short-circuits the conflict
+ * branch and its log line never runs. The end state is still correct - first
+ * registrar wins, the name is recorded once - but the conflict logging is
+ * effectively unreachable here.
+ */
+ $this->setExpectedIncorrectUsage( 'WP_Block_Type_Registry::register' );
+
+ $first = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+ $second = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $first ] ) )->register_blocks();
+ ( new SecondBlockRegistrar( [ $second ] ) )->register_blocks();
+
+ expect( BlockRegistrar::get_block_source( 'tenup-test/alpha' ) )
+ ->toBe( ConfigurableBlockRegistrar::class );
+
+ // Recorded once, so the conflicting registrar cannot double-add it to allowed blocks.
+ expect( array_count_values( BlockRegistrar::$registered_block_names )['tenup-test/alpha'] )->toBe( 1 );
+ }
+ );
+ }
+);
+
+describe(
+ 'allowed block types',
+ function (): void {
+ it(
+ 'adds registered blocks to the allowed list',
+ function (): void {
+ $dir = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $dir ] ) )->register_blocks();
+
+ expect( BlockRegistrar::filter_allowed_block_types( [ 'core/paragraph' ] ) )
+ ->toContain( 'core/paragraph' )
+ ->toContain( 'tenup-test/alpha' );
+ }
+ );
+
+ it(
+ 'passes a boolean through untouched',
+ function (): void {
+ // `true` means "all blocks allowed"; narrowing it to a list would be a regression.
+ expect( BlockRegistrar::filter_allowed_block_types( true ) )->toBeTrue();
+ expect( BlockRegistrar::filter_allowed_block_types( false ) )->toBeFalse();
+ }
+ );
+
+ it(
+ 'registers the allowed_block_types_all filter only once',
+ function (): void {
+ $first = tenup_make_blocks_dir( [ 'tenup-test/alpha' => [] ] );
+ $second = tenup_make_blocks_dir( [ 'tenup-test/beta' => [] ] );
+
+ ( new ConfigurableBlockRegistrar( [ $first ] ) )->register_blocks();
+ ( new SecondBlockRegistrar( [ $second ] ) )->register_blocks();
+
+ $callbacks = $GLOBALS['wp_filter']['allowed_block_types_all'][10] ?? [];
+
+ expect( $callbacks )->toHaveCount( 1 );
+ }
+ );
+ }
+);
diff --git a/tests/Integration/Core/EmojiTest.php b/tests/Integration/Core/EmojiTest.php
new file mode 100644
index 0000000..627d7e2
--- /dev/null
+++ b/tests/Integration/Core/EmojiTest.php
@@ -0,0 +1,170 @@
+emoji = new Emoji();
+ }
+);
+
+it(
+ 'is a module the loader will pick up',
+ function (): void {
+ expect( $this->emoji )->toBeInstanceOf( ModuleInterface::class );
+ expect( $this->emoji->can_register() )->toBeTrue();
+ expect( $this->emoji->load_order() )->toBe( 5 );
+ }
+);
+
+/*
+ * Asserted as whole sets rather than one test per hook. WordPress does not attach all of these
+ * in every version or request context — print_emoji_styles was deprecated out of core, and the
+ * admin_* hooks are not wired on a front-end request — so requiring each one to be attached up
+ * front makes the test fail on WordPress changes that are not regressions in this module. What
+ * matters is that none of them survive register(), plus a check that the set was not empty to
+ * begin with so the assertion cannot pass vacuously.
+ */
+const TENUP_EMOJI_ACTIONS = [
+ [ 'wp_head', 'print_emoji_detection_script' ],
+ [ 'admin_print_scripts', 'print_emoji_detection_script' ],
+ [ 'wp_print_styles', 'print_emoji_styles' ],
+ [ 'admin_print_styles', 'print_emoji_styles' ],
+];
+
+const TENUP_EMOJI_FILTERS = [
+ [ 'the_content_feed', 'wp_staticize_emoji' ],
+ [ 'comment_text_rss', 'wp_staticize_emoji' ],
+ [ 'wp_mail', 'wp_staticize_emoji_for_email' ],
+];
+
+it(
+ 'detaches every core emoji action it targets',
+ function (): void {
+ $attached_before = array_filter(
+ TENUP_EMOJI_ACTIONS,
+ static fn( array $hook ): bool => false !== has_action( $hook[0], $hook[1] )
+ );
+
+ expect( $attached_before )->not->toBeEmpty();
+
+ $this->emoji->register();
+
+ foreach ( TENUP_EMOJI_ACTIONS as [ $hook, $callback ] ) {
+ expect( has_action( $hook, $callback ) )->toBeFalse();
+ }
+ }
+);
+
+it(
+ 'detaches every emoji staticize filter it targets',
+ function (): void {
+ $attached_before = array_filter(
+ TENUP_EMOJI_FILTERS,
+ static fn( array $hook ): bool => false !== has_filter( $hook[0], $hook[1] )
+ );
+
+ expect( $attached_before )->not->toBeEmpty();
+
+ $this->emoji->register();
+
+ foreach ( TENUP_EMOJI_FILTERS as [ $hook, $callback ] ) {
+ expect( has_filter( $hook, $callback ) )->toBeFalse();
+ }
+ }
+);
+
+it(
+ 'attaches its own filters when registered',
+ function (): void {
+ $this->emoji->register();
+
+ expect( has_filter( 'tiny_mce_plugins', [ $this->emoji, 'disable_emojis_tinymce' ] ) )->not->toBeFalse();
+ expect( has_filter( 'wp_resource_hints', [ $this->emoji, 'disable_emoji_dns_prefetch' ] ) )->not->toBeFalse();
+ }
+);
+
+it(
+ 'removes wpemoji from the TinyMCE plugin list',
+ function (): void {
+ $result = $this->emoji->disable_emojis_tinymce( [ 'wordpress', 'wpemoji', 'media' ] );
+
+ expect( $result )->not->toContain( 'wpemoji' );
+ // phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText -- A literal TinyMCE plugin handle, not prose.
+ expect( $result )->toContain( 'wordpress' )->toContain( 'media' );
+ }
+);
+
+it(
+ 'leaves the TinyMCE plugin list untouched when wpemoji is absent',
+ function (): void {
+ $plugins = [ 'wordpress', 'media' ];
+
+ expect( $this->emoji->disable_emojis_tinymce( $plugins ) )->toBe( $plugins );
+ }
+);
+
+it(
+ 'drops the emoji CDN host from dns-prefetch hints',
+ function (): void {
+ $result = $this->emoji->disable_emoji_dns_prefetch(
+ [
+ 'https://fonts.googleapis.com',
+ 'https://s.w.org/images/core/emoji/2/svg/',
+ 'https://example.com',
+ ],
+ 'dns-prefetch'
+ );
+
+ expect( $result )->not->toContain( 'https://s.w.org/images/core/emoji/2/svg/' );
+ expect( $result )->toContain( 'https://fonts.googleapis.com' )->toContain( 'https://example.com' );
+ }
+);
+
+it(
+ 'leaves hints for other relation types untouched',
+ function (): void {
+ $urls = [ 'https://s.w.org/images/core/emoji/2/svg/', 'https://example.com' ];
+
+ expect( $this->emoji->disable_emoji_dns_prefetch( $urls, 'preconnect' ) )->toBe( $urls );
+ }
+);
+
+it(
+ 'takes effect through the wp_resource_hints filter once registered',
+ function (): void {
+ $this->emoji->register();
+
+ $hints = apply_filters(
+ 'wp_resource_hints',
+ [ 'https://s.w.org/images/core/emoji/2/svg/', 'https://example.com' ],
+ 'dns-prefetch'
+ );
+
+ expect( $hints )->not->toContain( 'https://s.w.org/images/core/emoji/2/svg/' );
+ }
+);
+
+it(
+ 'removes the emoji detection script from the rendered head',
+ function (): void {
+ $this->emoji->register();
+
+ ob_start();
+ do_action( 'wp_head' );
+ $head = (string) ob_get_clean();
+
+ expect( $head )->not->toContain( '_wpemojiSettings' );
+ }
+);
diff --git a/tests/Integration/Core/HeadOverridesTest.php b/tests/Integration/Core/HeadOverridesTest.php
new file mode 100644
index 0000000..3136b27
--- /dev/null
+++ b/tests/Integration/Core/HeadOverridesTest.php
@@ -0,0 +1,86 @@
+head_overrides = new HeadOverrides();
+
+ // Record what was actually attached before we start, so the assertions hold regardless
+ // of which of these WordPress still ships (wlwmanifest_link went away in WP 6.3).
+ $this->attached_before = array_filter(
+ TENUP_HEAD_CALLBACKS,
+ static fn( string $callback ): bool => false !== has_action( 'wp_head', $callback )
+ );
+ }
+);
+
+it(
+ 'is a module the loader will pick up',
+ function (): void {
+ expect( $this->head_overrides )->toBeInstanceOf( ModuleInterface::class );
+ expect( $this->head_overrides->can_register() )->toBeTrue();
+ // Ahead of taxonomies (9) and post types (10), since it only detaches core output.
+ expect( $this->head_overrides->load_order() )->toBe( 5 );
+ }
+);
+
+it(
+ 'detaches every wp_head callback it targets',
+ function (): void {
+ // Guard against a vacuous pass: if WordPress attached none of them, there is nothing to prove.
+ expect( $this->attached_before )->not->toBeEmpty();
+
+ $this->head_overrides->register();
+
+ foreach ( TENUP_HEAD_CALLBACKS as $callback ) {
+ expect( has_action( 'wp_head', $callback ) )->toBeFalse();
+ }
+ }
+);
+
+it(
+ 'removes the generator tag from the rendered head',
+ function (): void {
+ ob_start();
+ do_action( 'wp_head' );
+ $before = (string) ob_get_clean();
+
+ expect( $before )->toContain( ' head_overrides->register();
+
+ ob_start();
+ do_action( 'wp_head' );
+ $after = (string) ob_get_clean();
+
+ expect( $after )->not->toContain( ' head_overrides->register();
+
+ // A representative core callback it must not touch.
+ expect( has_action( 'wp_head', 'wp_robots' ) )->not->toBeFalse();
+ }
+);
diff --git a/tests/Integration/Debug/LoaderDebugTest.php b/tests/Integration/Debug/LoaderDebugTest.php
new file mode 100644
index 0000000..7d26ae4
--- /dev/null
+++ b/tests/Integration/Debug/LoaderDebugTest.php
@@ -0,0 +1,370 @@
+toBeTrue();
+ }
+ );
+
+ it(
+ 'is disabled when the filter returns false',
+ function (): void {
+ add_filter( 'tenup_framework_enable_loader_debug', '__return_false' );
+
+ expect( LoaderDebug::is_enabled() )->toBeFalse();
+
+ remove_filter( 'tenup_framework_enable_loader_debug', '__return_false' );
+ }
+ );
+
+ it(
+ 'stores nothing when disabled',
+ function (): void {
+ add_filter( 'tenup_framework_enable_loader_debug', '__return_false' );
+
+ LoaderDebug::record( tenup_sample_loader_record() );
+
+ expect( LoaderDebug::get_loaders() )->toBe( [] );
+
+ remove_filter( 'tenup_framework_enable_loader_debug', '__return_false' );
+ }
+ );
+ }
+);
+
+describe(
+ 'recording',
+ function (): void {
+ it(
+ 'stores a record when enabled',
+ function (): void {
+ LoaderDebug::record( tenup_sample_loader_record() );
+
+ $loaders = LoaderDebug::get_loaders();
+
+ expect( $loaders )->toHaveCount( 1 );
+ expect( $loaders[0]['directory'] )->toBe( '/srv/site/wp-content/plugins/demo/inc' );
+ }
+ );
+
+ it(
+ 'accumulates records for different directories',
+ function (): void {
+ LoaderDebug::record( tenup_sample_loader_record( '/a/inc' ) );
+ LoaderDebug::record( tenup_sample_loader_record( '/b/inc' ) );
+
+ expect( LoaderDebug::get_loaders() )->toHaveCount( 2 );
+ }
+ );
+
+ it(
+ 'replaces rather than duplicates a repeat record for one directory',
+ function (): void {
+ LoaderDebug::record( tenup_sample_loader_record( '/a/inc' ) );
+ LoaderDebug::record( tenup_sample_loader_record( '/a/inc' ) );
+
+ expect( LoaderDebug::get_loaders() )->toHaveCount( 1 );
+ }
+ );
+
+ it(
+ 'contributes its records to the cross-copy aggregation filter',
+ function (): void {
+ LoaderDebug::record( tenup_sample_loader_record( '/b/inc' ) );
+
+ // Records from another framework copy must survive alongside ours, which is the whole
+ // reason aggregation happens over a fixed-string filter instead of class references.
+ $merged = apply_filters( LoaderDebug::FILTER, [ [ 'directory' => '/a/inc' ] ] );
+
+ expect( $merged )->toHaveCount( 2 );
+ expect( $merged[0]['directory'] )->toBe( '/a/inc' );
+ expect( $merged[1]['directory'] )->toBe( '/b/inc' );
+ }
+ );
+ }
+);
+
+describe(
+ 'page rendering',
+ function (): void {
+ beforeEach(
+ function (): void {
+ tenup_acting_as_admin();
+ }
+ );
+
+ it(
+ 'lists each loader, its classes and its timings',
+ function (): void {
+ LoaderDebug::record( tenup_sample_loader_record() );
+
+ ob_start();
+ LoaderDebug::render_page();
+ $output = (string) ob_get_clean();
+
+ expect( $output )
+ ->toContain( 'WP Framework Loaders' )
+ ->toContain( '/srv/site/wp-content/plugins/demo/inc' )
+ ->toContain( 'TenupTmp\\Widget' )
+ ->toContain( 'Check this cache for staleness' )
+ ->toContain( 'Discovery time' )
+ ->toContain( 'Class lookup time' )
+ ->toContain( '12.30 ms' )
+ ->toContain( '45.60 ms' );
+ }
+ );
+
+ it(
+ 'says so when no loaders were recorded',
+ function (): void {
+ ob_start();
+ LoaderDebug::render_page();
+ $output = (string) ob_get_clean();
+
+ expect( $output )->toContain( 'No class loaders were recorded' );
+ }
+ );
+
+ it(
+ 'reports an up-to-date cache when the live scan matches',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+
+ $record = tenup_sample_loader_record( $dir );
+ $record['classes'] = [ 'TenupTmp\\Widget' ];
+ LoaderDebug::record( $record );
+
+ $_GET['check'] = tenup_invoke_loader_debug( 'token_for', [ $dir ] );
+ $_GET['_wpnonce'] = wp_create_nonce( LoaderDebug::CHECK_NONCE );
+
+ ob_start();
+ LoaderDebug::render_page();
+ $output = (string) ob_get_clean();
+
+ expect( $output )->toContain( 'Up to date' );
+ expect( $output )->toContain( 'Live discovery took' );
+
+ tenup_remove_dir( $dir );
+ }
+ );
+
+ it(
+ 'reports drift when the recorded classes differ from a live scan',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+
+ // A class the cache claims but which is not on disk, and a missing real one.
+ $record = tenup_sample_loader_record( $dir );
+ $record['classes'] = [ 'TenupTmp\\Ghost' ];
+ LoaderDebug::record( $record );
+
+ $_GET['check'] = tenup_invoke_loader_debug( 'token_for', [ $dir ] );
+ $_GET['_wpnonce'] = wp_create_nonce( LoaderDebug::CHECK_NONCE );
+
+ ob_start();
+ LoaderDebug::render_page();
+ $output = (string) ob_get_clean();
+
+ expect( $output )->toContain( 'Stale' );
+ expect( $output )->toContain( 'TenupTmp\\Ghost' );
+ expect( $output )->toContain( 'TenupTmp\\Widget' );
+
+ tenup_remove_dir( $dir );
+ }
+ );
+
+ it(
+ 'ignores a staleness check without a valid nonce',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+
+ LoaderDebug::record( tenup_sample_loader_record( $dir ) );
+
+ $_GET['check'] = tenup_invoke_loader_debug( 'token_for', [ $dir ] );
+ // No _wpnonce, so check_is_valid() must reject it.
+
+ ob_start();
+ LoaderDebug::render_page();
+ $output = (string) ob_get_clean();
+
+ expect( $output )->toContain( 'Check this cache for staleness' );
+ expect( $output )->not->toContain( 'Up to date' );
+
+ tenup_remove_dir( $dir );
+ }
+ );
+ }
+);
+
+describe(
+ 'formatting helpers',
+ function (): void {
+ it(
+ 'derives a plugin name for a directory under the plugins root',
+ function (): void {
+ expect( tenup_invoke_loader_debug( 'owner_label', [ WP_PLUGIN_DIR . '/demo/inc' ] ) )
+ ->toBe( 'Plugin: demo' );
+ }
+ );
+
+ it(
+ 'falls back to the raw directory when it is outside any known root',
+ function (): void {
+ expect( tenup_invoke_loader_debug( 'owner_label', [ '/somewhere/else/inc' ] ) )
+ ->toBe( '/somewhere/else/inc' );
+ }
+ );
+
+ it(
+ 'resolves the headline cache state',
+ function ( array $flags, string $severity, string $snippet ): void {
+ $state = tenup_invoke_loader_debug(
+ 'cache_state',
+ [ array_merge( tenup_sample_loader_record(), $flags ) ]
+ );
+
+ expect( $state['severity'] )->toBe( $severity );
+ expect( $state['badge'] )->toContain( $snippet );
+ }
+ )->with(
+ [
+ 'disabled' => [ [ 'cache_disabled' => true ], 'warn', 'disabled' ],
+ 'uncached' => [
+ [
+ 'cache_exists' => false,
+ 'cache_used' => false,
+ ],
+ 'warn',
+ 'Uncached',
+ ],
+ 'present but unused' => [
+ [
+ 'cache_exists' => true,
+ 'cache_used' => false,
+ ],
+ 'error',
+ 'not used',
+ ],
+ 'failed to load' => [
+ [
+ 'cache_exists' => true,
+ 'cache_used' => false,
+ 'cache_failed' => true,
+ ],
+ 'error',
+ 'failed to load',
+ ],
+ 'in use' => [
+ [
+ 'cache_exists' => true,
+ 'cache_used' => true,
+ ],
+ 'ok',
+ 'in use',
+ ],
+ ]
+ );
+
+ it(
+ 'formats a duration, rejecting anything not a positive finite number',
+ function ( mixed $seconds, string $expected ): void {
+ expect( tenup_invoke_loader_debug( 'format_duration', [ $seconds ] ) )->toBe( $expected );
+ }
+ )->with(
+ [
+ 'zero' => [ 0.0, '—' ],
+ 'negative' => [ -0.005, '—' ],
+ 'non-numeric' => [ 'nope', '—' ],
+ 'not-a-number' => [ NAN, '—' ],
+ 'infinite' => [ INF, '—' ],
+ 'sub-milli' => [ 0.0004, '0.400 ms' ],
+ 'milliseconds' => [ 0.0123, '12.30 ms' ],
+ 'seconds' => [ 1.5, '1.50 s' ],
+ ]
+ );
+
+ it(
+ 'reports unexpected files left in the cache directory',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+ $cache_dir = $dir . '/class-loader-cache';
+ mkdir( $cache_dir );
+
+ $current = $cache_dir . '/class-loader-cache-v2.php';
+ file_put_contents( $current, 'toBe( [ 'discoverer-cache-TenupFramework' ] );
+
+ tenup_remove_dir( $dir );
+ }
+ );
+
+ it(
+ 'reports no unexpected files when the cache directory is absent',
+ function (): void {
+ expect( tenup_invoke_loader_debug( 'legacy_files', [ '/nope/class-loader-cache/x.php' ] ) )
+ ->toBe( [] );
+ }
+ );
+
+ it(
+ 'describes the cache file with its size and UTC build time',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+ $cache_dir = $dir . '/class-loader-cache';
+ mkdir( $cache_dir );
+ $cache_file = $cache_dir . '/class-loader-cache-v2.php';
+ file_put_contents( $cache_file, ' $cache_file ] ] );
+
+ expect( $detail )->toContain( 'Built' );
+ // The absolute build time is the file mtime, rendered in UTC as the trailing segment.
+ expect( $detail )->toContain( '· ' . gmdate( 'Y-m-d H:i:s', (int) filemtime( $cache_file ) ) . ' UTC' );
+ expect( $detail )->toMatch( '/·\s*\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$/' );
+
+ tenup_remove_dir( $dir );
+ }
+ );
+
+ it(
+ 'says there is no cache file when none is on disk',
+ function (): void {
+ expect( tenup_invoke_loader_debug( 'cache_detail', [ [ 'cache_file' => '' ] ] ) )
+ ->toContain( 'No cache file on disk' );
+ }
+ );
+ }
+);
diff --git a/tests/Integration/ModuleInitialization/LoaderRecordingTest.php b/tests/Integration/ModuleInitialization/LoaderRecordingTest.php
new file mode 100644
index 0000000..b15b6ed
--- /dev/null
+++ b/tests/Integration/ModuleInitialization/LoaderRecordingTest.php
@@ -0,0 +1,105 @@
+dir = tenup_temp_class_dir();
+ }
+);
+
+afterEach(
+ function (): void {
+ tenup_remove_dir( $this->dir );
+ tenup_reset_framework_state();
+ }
+);
+
+it(
+ 'records a loader for the directory it discovered',
+ function (): void {
+ ModuleInitialization::instance()->init_classes( $this->dir );
+
+ $loaders = LoaderDebug::get_loaders();
+
+ expect( $loaders )->toHaveCount( 1 );
+ expect( $loaders[0]['directory'] )->toBe( $this->dir );
+ expect( $loaders[0]['classes'] )->toContain( 'TenupTmp\\Widget' );
+ }
+);
+
+it(
+ 'records live discovery timing when no cache exists',
+ function (): void {
+ ModuleInitialization::instance()->init_classes( $this->dir );
+
+ $loader = LoaderDebug::get_loaders()[0];
+
+ expect( $loader['cache_used'] )->toBeFalse();
+ // The never-wired default is 0.0, so strictly positive proves the instrumentation ran.
+ expect( $loader['discovery_seconds'] )->toBeFloat()->toBeGreaterThan( 0.0 );
+ expect( $loader['lookup_seconds'] )->toBeFloat()->toBeGreaterThan( 0.0 );
+ }
+);
+
+it(
+ 'reports the cache as in use when one is present',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+ $module_init->generate_cache( $this->dir );
+
+ $module_init->init_classes( $this->dir );
+
+ $loader = LoaderDebug::get_loaders()[0];
+
+ expect( $loader['cache_used'] )->toBeTrue();
+ expect( $loader['discovery_seconds'] )->toBeGreaterThan( 0.0 );
+ }
+);
+
+it(
+ 'flags a corrupt cache as failed rather than in use',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+ $module_init->generate_cache( $this->dir );
+ file_put_contents( tenup_cache_file_path( $this->dir ), 'init_classes( $this->dir );
+
+ $loader = LoaderDebug::get_loaders()[0];
+
+ expect( $loader['cache_failed'] )->toBeTrue();
+ expect( $loader['cache_used'] )->toBeFalse();
+ // Still found the real class on disk, because it fell back to a live scan.
+ expect( $loader['classes'] )->toContain( 'TenupTmp\\Widget' );
+ }
+);
+
+it(
+ 'keeps one record per directory when init_classes() runs twice',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+
+ $module_init->init_classes( $this->dir );
+ $module_init->init_classes( $this->dir );
+
+ expect( LoaderDebug::get_loaders() )->toHaveCount( 1 );
+ }
+);
diff --git a/tests/Integration/ModuleInitializationTest.php b/tests/Integration/ModuleInitializationTest.php
new file mode 100644
index 0000000..395d8f4
--- /dev/null
+++ b/tests/Integration/ModuleInitializationTest.php
@@ -0,0 +1,134 @@
+fixtures = dirname( __DIR__, 2 ) . '/fixtures/classes';
+ }
+);
+
+afterEach(
+ function (): void {
+ tenup_reset_framework_state();
+ }
+);
+
+it(
+ 'returns the same instance every time',
+ function (): void {
+ expect( ModuleInitialization::instance() )->toBe( ModuleInitialization::instance() );
+ }
+);
+
+it(
+ 'discovers the framework classes in a directory',
+ function (): void {
+ $classes = ModuleInitialization::instance()->get_classes( dirname( __DIR__, 2 ) . '/src/' );
+
+ expect( $classes )
+ ->toContain( 'TenupFramework\PostTypes\AbstractPostType' )
+ ->toContain( 'TenupFramework\PostTypes\AbstractCorePostType' )
+ ->toContain( 'TenupFramework\Taxonomies\AbstractTaxonomy' )
+ ->toContain( 'TenupFramework\ModuleInitialization' );
+ }
+);
+
+it(
+ 'registers only classes implementing ModuleInterface',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+ $module_init->init_classes( $this->fixtures );
+
+ $registered = $module_init->get_all_classes();
+
+ expect( $registered )->not->toBeEmpty();
+
+ foreach ( $registered as $instance ) {
+ expect( $instance )->toBeInstanceOf( ModuleInterface::class );
+ }
+ }
+);
+
+it(
+ 'fires the per-module init action and skips non-modules',
+ function (): void {
+ ModuleInitialization::instance()->init_classes( $this->fixtures );
+
+ expect( did_action( 'tenup_framework_module_init__tenupframeworktestclasses-posttypes-demo' ) )
+ ->toBeGreaterThan( 0 );
+
+ // Standalone implements no interface, so it must never be instantiated or announced.
+ expect( did_action( 'tenup_framework_module_init__tenupframeworktestclasses-standalone-standalone' ) )
+ ->toBe( 0 );
+ }
+);
+
+it(
+ 'exposes registered modules through get_module()',
+ function (): void {
+ ModuleInitialization::instance()->init_classes( $this->fixtures );
+
+ expect( ModuleInitialization::get_module( Demo::class ) )->toBeInstanceOf( Demo::class );
+ expect( ModuleInitialization::get_module( 'TenupFrameworkTestClasses\DoesntExist' ) )->toBeFalse();
+ }
+);
+
+it(
+ 'reflects loadable classes and rejects un-loadable ones',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+
+ expect( $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\BaseClass' ) )
+ ->toBeInstanceOf( ReflectionClass::class );
+ expect( $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\ChildClass' ) )
+ ->toBeInstanceOf( ReflectionClass::class );
+ // Extends a parent that does not exist, so reflection throws and false is returned.
+ expect( $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\InvalidChildClass' ) )
+ ->toBeFalse();
+ }
+);
+
+it(
+ 'throws when the directory does not exist',
+ function (): void {
+ ModuleInitialization::instance()->init_classes( dirname( __DIR__, 2 ) . '/src/does-not-exist-1234567/' );
+ }
+)->throws( RuntimeException::class );
+
+it(
+ 'throws when no directory is passed',
+ function (): void {
+ ModuleInitialization::instance()->init_classes();
+ }
+)->throws( RuntimeException::class );
+
+// The admin-only counterpart lives in ModuleInitialization/LoaderRecordingTest.php, which
+// applies Mantle's Admin_Screen trait to make is_admin() true for the whole file.
+it(
+ 'records no loader debug data on the front end',
+ function (): void {
+ $dir = tenup_temp_class_dir();
+
+ ModuleInitialization::instance()->init_classes( $dir );
+
+ expect( LoaderDebug::get_loaders() )->toBe( [] );
+
+ tenup_remove_dir( $dir );
+ }
+);
diff --git a/tests/Integration/PostTypes/AbstractPostTypeTest.php b/tests/Integration/PostTypes/AbstractPostTypeTest.php
new file mode 100644
index 0000000..040a65f
--- /dev/null
+++ b/tests/Integration/PostTypes/AbstractPostTypeTest.php
@@ -0,0 +1,154 @@
+post_type = new Demo();
+ }
+);
+
+afterEach(
+ function (): void {
+ // Post type and taxonomy registries are process globals, not database state, so the
+ // transaction rollback between tests does not clear them.
+ if ( post_type_exists( 'tenup-demo' ) ) {
+ unregister_post_type( 'tenup-demo' );
+ }
+
+ if ( taxonomy_exists( 'tenup-tax-demo' ) ) {
+ unregister_taxonomy( 'tenup-tax-demo' );
+ }
+ }
+);
+
+it(
+ 'registers the post type with WordPress',
+ function (): void {
+ expect( post_type_exists( 'tenup-demo' ) )->toBeFalse();
+
+ $this->post_type->register();
+
+ expect( post_type_exists( 'tenup-demo' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'uses the plural label for name and the singular for singular_name',
+ function (): void {
+ $this->post_type->register();
+
+ $labels = get_post_type_object( 'tenup-demo' )->labels;
+
+ expect( $labels->name )->toBe( 'Demos' );
+ expect( $labels->singular_name )->toBe( 'Demo' );
+ }
+);
+
+it(
+ 'derives the remaining labels from the singular and plural labels',
+ function (): void {
+ $this->post_type->register();
+
+ $labels = get_post_type_object( 'tenup-demo' )->labels;
+
+ expect( $labels->add_new_item )->toBe( 'Add New Demo' );
+ expect( $labels->edit_item )->toBe( 'Edit Demo' );
+ expect( $labels->view_items )->toBe( 'View Demos' );
+ expect( $labels->all_items )->toBe( 'All Demos' );
+ expect( $labels->archives )->toBe( 'Demo Archives' );
+ // Lower-cased in the sentence-style labels.
+ expect( $labels->not_found )->toBe( 'No demos found.' );
+ expect( $labels->insert_into_item )->toBe( 'Insert into demo' );
+ }
+);
+
+it(
+ 'applies the default options from get_options()',
+ function (): void {
+ $this->post_type->register();
+
+ $object = get_post_type_object( 'tenup-demo' );
+
+ expect( $object->public )->toBeTrue();
+ expect( $object->has_archive )->toBeTrue();
+ expect( $object->show_ui )->toBeTrue();
+ expect( $object->show_in_menu )->toBeTrue();
+ expect( $object->show_in_rest )->toBeTrue();
+ // Deliberately false by default, unlike most of the flags above.
+ expect( $object->show_in_nav_menus )->toBeFalse();
+ expect( $object->hierarchical )->toBeFalse();
+ }
+);
+
+it(
+ 'sets the menu icon declared by the subclass',
+ function (): void {
+ $this->post_type->register();
+
+ expect( get_post_type_object( 'tenup-demo' )->menu_icon )->toBe( 'dashicons-chart-pie' );
+ }
+);
+
+it(
+ 'registers the default editor supports',
+ function (): void {
+ $this->post_type->register();
+
+ foreach ( [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'revisions' ] as $feature ) {
+ expect( post_type_supports( 'tenup-demo', $feature ) )->toBeTrue();
+ }
+
+ expect( post_type_supports( 'tenup-demo', 'comments' ) )->toBeFalse();
+ }
+);
+
+it(
+ 'associates the taxonomies the post type declares',
+ function (): void {
+ // The taxonomy has to exist before register_taxonomy_for_object_type() will attach it,
+ // which is exactly why AbstractTaxonomy uses load order 9 and post types use 10.
+ ( new DemoTaxonomy() )->register();
+
+ $this->post_type->register();
+
+ expect( get_object_taxonomies( 'tenup-demo' ) )->toContain( 'tenup-tax-demo' );
+ }
+);
+
+it(
+ 'produces a post type that can actually store and retrieve a post',
+ function (): void {
+ $this->post_type->register();
+
+ $post_id = wp_insert_post(
+ [
+ 'post_type' => 'tenup-demo',
+ 'post_title' => 'A demo item',
+ 'post_status' => 'publish',
+ ]
+ );
+
+ expect( $post_id )->toBeInt()->toBeGreaterThan( 0 );
+ expect( get_post( $post_id )->post_type )->toBe( 'tenup-demo' );
+ }
+);
+
+it(
+ 'registers after taxonomies via its load order',
+ function (): void {
+ expect( $this->post_type->load_order() )->toBe( 10 );
+ expect( ( new DemoTaxonomy() )->load_order() )->toBeLessThan( $this->post_type->load_order() );
+ }
+);
diff --git a/tests/Integration/SmokeTest.php b/tests/Integration/SmokeTest.php
new file mode 100644
index 0000000..5cf1977
--- /dev/null
+++ b/tests/Integration/SmokeTest.php
@@ -0,0 +1,27 @@
+toBeTrue();
+ expect( function_exists( 'register_post_type' ) )->toBeTrue();
+ expect( did_action( 'init' ) )->toBeGreaterThan( 0 );
+ }
+);
+
+it(
+ 'has a usable database',
+ function (): void {
+ global $wpdb;
+
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Proving the test database is reachable is the point.
+ expect( $wpdb->get_var( 'SELECT 1' ) )->toEqual( 1 );
+ }
+);
diff --git a/tests/Integration/Taxonomies/AbstractTaxonomyTest.php b/tests/Integration/Taxonomies/AbstractTaxonomyTest.php
new file mode 100644
index 0000000..53b5e52
--- /dev/null
+++ b/tests/Integration/Taxonomies/AbstractTaxonomyTest.php
@@ -0,0 +1,110 @@
+taxonomy = new Demo();
+ }
+);
+
+afterEach(
+ function (): void {
+ if ( taxonomy_exists( 'tenup-tax-demo' ) ) {
+ unregister_taxonomy( 'tenup-tax-demo' );
+ }
+ }
+);
+
+it(
+ 'registers the taxonomy with WordPress',
+ function (): void {
+ expect( taxonomy_exists( 'tenup-tax-demo' ) )->toBeFalse();
+
+ $this->taxonomy->register();
+
+ expect( taxonomy_exists( 'tenup-tax-demo' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'uses the plural label for name and the singular for singular_name',
+ function (): void {
+ $this->taxonomy->register();
+
+ $labels = get_taxonomy( 'tenup-tax-demo' )->labels;
+
+ expect( $labels->name )->toBe( 'Demo Terms' );
+ expect( $labels->singular_name )->toBe( 'Demo Term' );
+ }
+);
+
+it(
+ 'derives the remaining labels from the singular and plural labels',
+ function (): void {
+ $this->taxonomy->register();
+
+ $labels = get_taxonomy( 'tenup-tax-demo' )->labels;
+
+ expect( $labels->search_items )->toBe( 'Search Demo Terms' );
+ expect( $labels->add_new_item )->toBe( 'Add Demo Term' );
+ expect( $labels->new_item_name )->toBe( 'New Demo Term Name' );
+ expect( $labels->all_items )->toBe( 'All Demo Terms' );
+ // Lower-cased in the sentence-style labels.
+ expect( $labels->separate_items_with_commas )->toBe( 'Separate demo terms with commas' );
+ expect( $labels->not_found )->toBe( 'No demo terms found.' );
+ }
+);
+
+it(
+ 'applies the default options from get_options()',
+ function (): void {
+ $this->taxonomy->register();
+
+ $object = get_taxonomy( 'tenup-tax-demo' );
+
+ expect( $object->public )->toBeTrue();
+ expect( $object->show_ui )->toBeTrue();
+ expect( $object->show_admin_column )->toBeTrue();
+ expect( $object->show_in_rest )->toBeTrue();
+ expect( $object->hierarchical )->toBeFalse();
+ expect( $object->query_var )->toBe( 'tenup-tax-demo' );
+ }
+);
+
+it(
+ 'attaches to no post types by default, leaving that to the post type classes',
+ function (): void {
+ expect( $this->taxonomy->get_post_types() )->toBe( [] );
+
+ $this->taxonomy->register();
+
+ expect( get_taxonomy( 'tenup-tax-demo' )->object_type )->toBe( [] );
+ }
+);
+
+it(
+ 'registers before post types via its load order',
+ function (): void {
+ expect( $this->taxonomy->load_order() )->toBe( 9 );
+ }
+);
+
+it(
+ 'produces a taxonomy that can actually store and retrieve a term',
+ function (): void {
+ $this->taxonomy->register();
+
+ $term = wp_insert_term( 'Example Term', 'tenup-tax-demo' );
+
+ expect( $term )->toBeArray();
+ expect( get_term( $term['term_id'], 'tenup-tax-demo' )->name )->toBe( 'Example Term' );
+ }
+);
diff --git a/tests/ModuleInitializationTest.php b/tests/ModuleInitializationTest.php
deleted file mode 100644
index 5f054ff..0000000
--- a/tests/ModuleInitializationTest.php
+++ /dev/null
@@ -1,514 +0,0 @@
-get_classes( dirname( __DIR__, 1 ) . '/src/' );
-
- // Check that we have the concrete classes we expect to see.
- $this->assertContains( 'TenupFramework\PostTypes\AbstractPostType', $classes );
- $this->assertContains( 'TenupFramework\PostTypes\AbstractCorePostType', $classes );
- $this->assertContains( 'TenupFramework\Taxonomies\AbstractTaxonomy', $classes );
- $this->assertContains( 'TenupFramework\ModuleInitialization', $classes );
- }
-
- /**
- * Ensure we can find the right classes.
- *
- * @return void
- */
- public function test_it_can_find_classes_to_register() {
- $class = \TenupFramework\ModuleInitialization::instance();
- $class->init_classes( dirname( __DIR__, 1 ) . '/fixtures/classes' );
- $classes = $class->get_all_classes();
-
- // The registered set is non-empty and contains only ModuleInterface implementations.
- $this->assertNotEmpty( $classes );
- foreach ( $classes as $registered ) {
- $this->assertInstanceOf( \TenupFramework\ModuleInterface::class, $registered );
- }
- }
-
- /**
- * Ensure an exception is thrown when a directory does not exist.
- *
- * @return void
- */
- public function test_that_an_exception_is_thrown_when_a_directory_does_not_exist() {
- $class = \TenupFramework\ModuleInitialization::instance();
- $this->expectException( \RuntimeException::class );
- $class->init_classes( dirname( __DIR__, 1 ) . '/src/does-not-exist-1234567/' );
- }
-
- /**
- * Ensure an exception is thrown when a directory is not passed.
- *
- * @return void
- */
- public function test_that_an_exception_is_thrown_when_a_directory_is_not_passed() {
- $class = \TenupFramework\ModuleInitialization::instance();
- $this->expectException( \RuntimeException::class );
- $class->init_classes();
- }
-
- /**
- * Ensure the instance method returns the same instance.
- *
- * @return void
- */
- public function test_instance_returns_same_instance() {
- $instance1 = \TenupFramework\ModuleInitialization::instance();
- $instance2 = \TenupFramework\ModuleInitialization::instance();
- $this->assertSame( $instance1, $instance2 );
- }
-
- /**
- * Ensure the instance method returns the same instance.
- *
- * @return void
- */
- public function test_get_classes_returns_classes_from_directory() {
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $classes = $module_init->get_classes( dirname( __DIR__, 1 ) . '/fixtures/classes' );
- $this->assertIsArray( $classes );
- $this->assertNotEmpty( $classes );
- }
-
- /**
- * Ensure the instance method returns the same instance.
- *
- * @return void
- */
- public function test_init_classes_initializes_classes_in_correct_order() {
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->init_classes( dirname( __DIR__, 1 ) . '/fixtures/classes' );
- $classes = $module_init->get_all_classes();
- $this->assertNotEmpty( $classes );
- $this->assertNotContains( 'TenupFramework\Taxonomies\AbstractTaxonomy', $classes );
- $this->assertInstanceOf( \TenupFramework\ModuleInterface::class, reset( $classes ) );
- }
-
- /**
- * Ensure that we can return an instantiated class vie get_module.
- *
- * @return void
- */
- public function test_get_module_returns_instantiated_class() {
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->init_classes( dirname( __DIR__, 1 ) . '/fixtures/classes' );
- $module = \TenupFramework\ModuleInitialization::get_module( 'TenupFrameworkTestClasses\PostTypes\Demo' );
- $this->assertInstanceOf( \TenupFrameworkTestClasses\PostTypes\Demo::class, $module );
-
- $module = \TenupFramework\ModuleInitialization::get_module( 'TenupFrameworkTestClasses\DoesntExist' );
- $this->assertFalse( $module );
- }
-
- /**
- * Test that only classes implementing ModuleInterface are initialized.
- *
- * @return void
- */
- public function test_only_classes_implementing_module_interface_are_initialized() {
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->init_classes( dirname( __DIR__, 1 ) . '/fixtures/classes' );
-
- $this->assertTrue( did_action( 'tenup_framework_module_init__tenupframeworktestclasses-posttypes-demo' ) > 0, 'Demo was not initialized.' );
- $this->assertFalse( did_action( 'tenup_framework_module_init__tenupframeworktestclasses-standalone-standalone' ) > 0, 'Standalone class was initialized.' );
- }
-
- /**
- * Validate if the classes are fully loadable.
- *
- * @return void
- */
- public function testIsClassFullyLoadable() {
- $module_init = \TenupFramework\ModuleInitialization::instance();
-
- $this->assertInstanceOf( 'ReflectionClass', $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\BaseClass' ) );
- $this->assertInstanceOf( 'ReflectionClass', $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\ChildClass' ) );
- $this->assertFalse( $module_init->get_fully_loadable_class( '\TenupFrameworkTestClasses\Loadable\InvalidChildClass' ) );
- }
-
-
- /**
- * generate_cache() writes a readable cache file and returns the discovered classes.
- *
- * @return void
- */
- public function test_generate_cache_writes_a_readable_cache_file() {
- $dir = $this->make_temp_class_dir();
-
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $cached = $module_init->generate_cache( $dir );
-
- $this->assertFileExists( $this->cache_file_path( $dir ) );
- $this->assertContains( 'TenupTmp\\Widget', $cached );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * The runtime read path uses the cache file when one is present.
- *
- * @return void
- */
- public function test_get_classes_reads_the_cache_file_when_present() {
- $dir = $this->make_temp_class_dir();
-
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->generate_cache( $dir );
-
- // Tamper with the cache so we can prove the read path uses it rather than re-discovering.
- $this->write_file( $this->cache_file_path( $dir ), "get_classes( $dir );
-
- $this->assertSame( [ 'TenupTmp\\Sentinel' ], array_values( $read ) );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * With no cache present the runtime discovers live and writes nothing.
- *
- * @return void
- */
- public function test_get_classes_creates_no_cache_when_none_exists() {
- $dir = $this->make_temp_class_dir();
-
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $classes = $module_init->get_classes( $dir );
-
- $this->assertContains( 'TenupTmp\\Widget', $classes );
- $this->assertDirectoryDoesNotExist( $dir . '/' . \TenupFramework\ModuleInitialization::CACHE_DIR_NAME );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * A cache written by an older framework version (a different filename) is ignored,
- * so an upgraded site never serves a stale cache it cannot rewrite.
- *
- * @return void
- */
- public function test_get_classes_ignores_legacy_cache_file() {
- $dir = $this->make_temp_class_dir();
- $cache_dir = $dir . '/' . \TenupFramework\ModuleInitialization::CACHE_DIR_NAME;
- mkdir( $cache_dir );
-
- // The previous version wrote `discoverer-cache-{id}` as a serialized file.
- $this->write_file( $cache_dir . '/discoverer-cache-TenupFramework', serialize( [ 'TenupTmp\\Legacy' ] ) );
-
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $read = $module_init->get_classes( $dir );
-
- $this->assertNotContains( 'TenupTmp\\Legacy', $read );
- $this->assertContains( 'TenupTmp\\Widget', $read );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * A corrupt or truncated cache file does not fatal the request: get_classes() catches the
- * error and falls back to a live discovery, so a bad cache degrades to uncached rather than
- * taking the site down.
- *
- * @return void
- */
- public function test_get_classes_falls_back_to_live_when_cache_is_corrupt() {
- $dir = $this->make_temp_class_dir();
- $cache_dir = $dir . '/' . \TenupFramework\ModuleInitialization::CACHE_DIR_NAME;
- mkdir( $cache_dir );
-
- // A truncated / syntactically broken cache file — `require` on this throws a ParseError.
- $this->write_file(
- $cache_dir . '/' . \TenupFramework\ModuleInitialization::CACHE_FILENAME,
- "get_classes( $dir );
-
- // Fell back to a live scan and still found the real class on disk.
- $this->assertContains( 'TenupTmp\\Widget', $read );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * Defining TENUP_FRAMEWORK_DISABLE_CLASS_CACHE forces live discovery even when a
- * cache file is present.
- *
- * @return void
- */
- public function test_disable_constant_forces_live_discovery() {
- $dir = $this->make_temp_class_dir();
-
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->generate_cache( $dir );
-
- // Tamper with the cache; with caching disabled this sentinel must not be read.
- $this->write_file( $this->cache_file_path( $dir ), "get_classes( $dir );
-
- $this->assertNotContains( 'TenupTmp\\Sentinel', $read );
- $this->assertContains( 'TenupTmp\\Widget', $read );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * In the admin, init_classes() hands a loader record to the debug registry.
- *
- * @return void
- */
- public function test_init_classes_records_a_loader_in_admin() {
- when( 'is_admin' )->justReturn( true );
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
-
- $dir = $this->make_temp_class_dir();
-
- \TenupFramework\ModuleInitialization::instance()->init_classes( $dir );
-
- $loaders = \TenupFramework\Debug\LoaderDebug::get_loaders();
- $this->assertCount( 1, $loaders );
- $this->assertSame( $dir, $loaders[0]['directory'] );
- $this->assertContains( 'TenupTmp\\Widget', $loaders[0]['classes'] );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * With no cache present, init_classes() records the time of a genuine live (uncached)
- * discovery — a real filesystem scan, so the recorded duration is strictly positive.
- *
- * @return void
- */
- public function test_init_classes_records_live_discovery_timing_in_admin() {
- when( 'is_admin' )->justReturn( true );
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
-
- $dir = $this->make_temp_class_dir();
-
- \TenupFramework\ModuleInitialization::instance()->init_classes( $dir );
-
- $loaders = \TenupFramework\Debug\LoaderDebug::get_loaders();
- $this->assertCount( 1, $loaders );
- $this->assertFalse( $loaders[0]['cache_used'], 'No cache exists, so discovery must be live.' );
- $this->assertIsFloat( $loaders[0]['discovery_seconds'] );
- $this->assertIsFloat( $loaders[0]['lookup_seconds'] );
- // A live filesystem scan and the reflection loop both take measurable time; the never-wired
- // default is 0.0, so asserting strictly-positive proves the instrumentation actually ran.
- $this->assertGreaterThan( 0.0, $loaders[0]['discovery_seconds'] );
- $this->assertGreaterThan( 0.0, $loaders[0]['lookup_seconds'] );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * With a pre-built cache present, init_classes() reads it (cache_used) and still records a
- * positive discovery duration — the cache-read cost rather than a live scan.
- *
- * @return void
- */
- public function test_init_classes_records_cache_read_timing_in_admin() {
- when( 'is_admin' )->justReturn( true );
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
-
- $dir = $this->make_temp_class_dir();
- $module = \TenupFramework\ModuleInitialization::instance();
- $module->generate_cache( $dir );
-
- $module->init_classes( $dir );
-
- $loaders = \TenupFramework\Debug\LoaderDebug::get_loaders();
- $this->assertCount( 1, $loaders );
- $this->assertTrue( $loaders[0]['cache_used'], 'A cache file exists, so it should be used.' );
- $this->assertGreaterThan( 0.0, $loaders[0]['discovery_seconds'] );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * On the front end, init_classes() records nothing (the data is only viewable in the admin).
- *
- * @return void
- */
- public function test_init_classes_records_nothing_on_the_front_end() {
- when( 'is_admin' )->justReturn( false );
-
- $dir = $this->make_temp_class_dir();
-
- \TenupFramework\ModuleInitialization::instance()->init_classes( $dir );
-
- $this->assertSame( [], \TenupFramework\Debug\LoaderDebug::get_loaders() );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * discover_live() ignores any cache file and returns the real on-disk classes.
- *
- * @return void
- */
- public function test_discover_live_ignores_the_cache() {
- $dir = $this->make_temp_class_dir();
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->generate_cache( $dir );
-
- // Tamper with the cache; discover_live() must not read it.
- $this->write_file( $this->cache_file_path( $dir ), "discover_live( $dir );
-
- $this->assertContains( 'TenupTmp\\Widget', $live );
- $this->assertNotContains( 'TenupTmp\\Sentinel', $live );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * A corrupt or truncated cache file falls back to a live scan instead of fataling.
- *
- * @return void
- */
- public function test_get_classes_falls_back_when_the_cache_is_corrupt() {
- $dir = $this->make_temp_class_dir();
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->generate_cache( $dir );
-
- // Truncated PHP: require() raises a ParseError, which the read path must catch.
- $this->write_file( $this->cache_file_path( $dir ), 'get_classes( $dir );
-
- $this->assertContains( 'TenupTmp\\Widget', $classes );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * When the read path falls back, the loader record flags the failure so the debug page can
- * show it rather than reporting the cache as in use.
- *
- * @return void
- */
- public function test_corrupt_cache_records_a_failed_state() {
- when( 'is_admin' )->justReturn( true );
- when( 'add_action' )->justReturn( true );
- when( 'add_filter' )->justReturn( true );
- when( 'apply_filters' )->returnArg( 2 );
-
- $dir = $this->make_temp_class_dir();
- $module_init = \TenupFramework\ModuleInitialization::instance();
- $module_init->generate_cache( $dir );
- $this->write_file( $this->cache_file_path( $dir ), 'init_classes( $dir );
-
- $loaders = \TenupFramework\Debug\LoaderDebug::get_loaders();
- $this->assertNotEmpty( $loaders );
- $this->assertTrue( $loaders[0]['cache_failed'] );
- $this->assertFalse( $loaders[0]['cache_used'] );
- $this->assertContains( 'TenupTmp\\Widget', $loaders[0]['classes'] );
-
- $this->remove_temp_dir( $dir );
- }
-
- /**
- * Build the absolute path to the cache file for a discovery directory.
- *
- * @param string $dir The discovery directory.
- *
- * @return string
- */
- private function cache_file_path( string $dir ): string {
- return $dir . '/' . \TenupFramework\ModuleInitialization::CACHE_DIR_NAME
- . '/' . \TenupFramework\ModuleInitialization::CACHE_FILENAME;
- }
-
- /**
- * Create a temporary directory containing a single discoverable class.
- *
- * @return string The created directory path.
- */
- private function make_temp_class_dir(): string {
- $dir = sys_get_temp_dir() . '/tenup_framework_test_' . uniqid( '', true );
- mkdir( $dir );
- $this->write_file( $dir . '/Widget.php', "assertNotFalse( file_put_contents( $path, $contents ) );
- }
-
- /**
- * Recursively remove a temporary directory.
- *
- * @param string $dir The directory to remove.
- *
- * @return void
- */
- private function remove_temp_dir( string $dir ): void {
- if ( ! is_dir( $dir ) ) {
- return;
- }
-
- $items = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator( $dir, \FilesystemIterator::SKIP_DOTS ),
- \RecursiveIteratorIterator::CHILD_FIRST
- );
-
- foreach ( $items as $item ) {
- if ( $item->isDir() ) {
- rmdir( $item->getPathname() );
- } else {
- unlink( $item->getPathname() );
- }
- }
-
- rmdir( $dir );
- }
-}
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..3d6920a
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,21 @@
+extend( Integration_Test_Case::class )->in( 'Integration' );
+pest()->extend( Unit_Test_Case::class )->in( 'Unit' );
diff --git a/tests/PostTypes/AbstractPostTypeTest.php b/tests/PostTypes/AbstractPostTypeTest.php
deleted file mode 100644
index 7cd0071..0000000
--- a/tests/PostTypes/AbstractPostTypeTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-register();
-
- $this->assertArrayHasKey( $class->get_name(), self::$registered_post_types );
- $this->assertEquals( $class->get_plural_label(), self::$registered_post_types['tenup-demo']['labels']['name'] );
- $this->assertEquals( $class->get_singular_label(), self::$registered_post_types['tenup-demo']['labels']['singular_name'] );
- }
-}
diff --git a/tests/Taxonomies/AbstractTaxonomyTest.php b/tests/Taxonomies/AbstractTaxonomyTest.php
deleted file mode 100644
index 5c6f4b0..0000000
--- a/tests/Taxonomies/AbstractTaxonomyTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-register();
-
- $this->assertArrayHasKey( $class->get_name(), self::$registered_taxonomies );
- $this->assertEquals( $class->get_plural_label(), self::$registered_taxonomies['tenup-tax-demo']['labels']['name'] );
- $this->assertEquals( $class->get_singular_label(), self::$registered_taxonomies['tenup-tax-demo']['labels']['singular_name'] );
- }
-}
diff --git a/tests/TestBlockRegistrar.php b/tests/TestBlockRegistrar.php
deleted file mode 100644
index 0167912..0000000
--- a/tests/TestBlockRegistrar.php
+++ /dev/null
@@ -1,26 +0,0 @@
-asset_info = tenup_asset_info_consumer();
+ $this->asset_info->setup_asset_vars(
+ dist_path: tenup_asset_fixture_path(),
+ fallback_version: '1.0.0'
+ );
+ }
+);
+
+it(
+ 'normalises the dist path and stores the fallback version',
+ function (): void {
+ $asset_info = tenup_asset_info_consumer();
+
+ $asset_info->setup_asset_vars( dist_path: 'dist', fallback_version: '1.0.0' );
+
+ expect( $asset_info->dist_path )->toBe( 'dist/' );
+ expect( $asset_info->fallback_version )->toBe( '1.0.0' );
+ }
+);
+
+it(
+ 'throws when get_asset_info() is called before setup_asset_vars()',
+ function (): void {
+ tenup_asset_info_consumer()->get_asset_info( slug: 'test-script' );
+ }
+)->throws(
+ RuntimeException::class,
+ 'Asset variables not set. Please run setup_asset_vars() before calling get_asset_info().'
+);
+
+it(
+ 'resolves a bare slug by searching js/, then css/, then blocks/',
+ function ( string $slug, string $subdirectory ): void {
+ $expected = require tenup_asset_fixture_path() . "/{$subdirectory}/{$slug}.asset.php";
+
+ expect( $this->asset_info->get_asset_info( slug: $slug ) )->toBe( $expected );
+ }
+)->with(
+ [
+ 'js sidecar' => [ 'test-script', 'js' ],
+ 'css sidecar' => [ 'test-style', 'css' ],
+ 'blocks sidecar' => [ 'test-block', 'blocks' ],
+ ]
+);
+
+it(
+ 'resolves an explicitly prefixed slug',
+ function ( string $slug, string $subdirectory, string $file ): void {
+ $expected = require tenup_asset_fixture_path() . "/{$subdirectory}/{$file}.asset.php";
+
+ expect( $this->asset_info->get_asset_info( slug: $slug ) )->toBe( $expected );
+ }
+)->with(
+ [
+ 'css prefix' => [ 'css/test-style', 'css', 'test-style' ],
+ 'js prefix' => [ 'js/test-script', 'js', 'test-script' ],
+ 'blocks prefix' => [ 'blocks/test-block', 'blocks', 'test-block' ],
+ ]
+);
+
+it(
+ 'returns a single attribute when one is requested',
+ function (): void {
+ expect( $this->asset_info->get_asset_info( slug: 'test-script', attribute: 'version' ) )
+ ->toBe( 'test-script-version' );
+
+ expect( $this->asset_info->get_asset_info( slug: 'test-script', attribute: 'dependencies' ) )
+ ->toBe( [ 'test-script-deps' ] );
+ }
+);
+
+it(
+ 'falls back to the supplied version and no dependencies when no sidecar exists',
+ function (): void {
+ expect( $this->asset_info->get_asset_info( slug: 'non-existent' ) )
+ ->toBe(
+ [
+ 'version' => '1.0.0',
+ 'dependencies' => [],
+ ]
+ );
+ }
+);
diff --git a/tests/Unit/Bin/GenerateClassCacheTest.php b/tests/Unit/Bin/GenerateClassCacheTest.php
new file mode 100644
index 0000000..ac2e98c
--- /dev/null
+++ b/tests/Unit/Bin/GenerateClassCacheTest.php
@@ -0,0 +1,94 @@
+temp_dirs = [];
+ }
+);
+
+afterEach(
+ function (): void {
+ foreach ( $this->temp_dirs as $dir ) {
+ tenup_remove_dir( $dir );
+ }
+ }
+);
+
+it(
+ 'generates a readable cache of the classes in a directory',
+ function (): void {
+ $dir = tenup_example_copy( 'plugin-inc' );
+ $this->temp_dirs[] = $dir;
+
+ $result = tenup_run_cache_bin( [ $dir ] );
+
+ expect( $result['exit'] )->toBe( 0, $result['stderr'] );
+ expect( $result['stdout'] )->toContain( 'Cached' );
+
+ $cache_file = tenup_cache_file_path( $dir );
+ expect( file_exists( $cache_file ) )->toBeTrue();
+
+ $cached = require $cache_file;
+ expect( $cached )
+ ->toContain( 'TenupFrameworkExamples\\Modules\\GreetingModule' )
+ ->toContain( 'TenupFrameworkExamples\\Support\\Formatter' );
+ }
+);
+
+it(
+ 'reports usage and fails when given no arguments',
+ function (): void {
+ $result = tenup_run_cache_bin( [] );
+
+ expect( $result['exit'] )->toBe( 1 );
+ expect( $result['stderr'] )->toContain( 'Usage:' );
+ }
+);
+
+it(
+ 'still caches the valid directories when one is missing',
+ function (): void {
+ $good = tenup_example_copy( 'plugin-inc' );
+ $this->temp_dirs[] = $good;
+ $missing = sys_get_temp_dir() . '/tenup_bin_missing_' . uniqid( '', true );
+
+ // The missing directory is passed first, to prove a failure does not abort the run.
+ $result = tenup_run_cache_bin( [ $missing, $good ] );
+
+ expect( $result['exit'] )->toBe( 1 );
+ expect( $result['stderr'] )
+ ->toContain( $missing )
+ ->toContain( 'Failed to generate cache' );
+
+ expect( file_exists( tenup_cache_file_path( $good ) ) )->toBeTrue();
+ }
+);
+
+it(
+ 'caches several directories in a single invocation',
+ function (): void {
+ $first = tenup_example_copy( 'plugin-inc' );
+ $second = tenup_example_copy( 'second-inc' );
+ $this->temp_dirs[] = $first;
+ $this->temp_dirs[] = $second;
+
+ $result = tenup_run_cache_bin( [ $first, $second ] );
+
+ expect( $result['exit'] )->toBe( 0, $result['stderr'] );
+ expect( file_exists( tenup_cache_file_path( $first ) ) )->toBeTrue();
+ expect( file_exists( tenup_cache_file_path( $second ) ) )->toBeTrue();
+
+ $cached = require tenup_cache_file_path( $second );
+ expect( $cached )->toContain( 'TenupFrameworkExamples\\Widgets\\Card' );
+ }
+);
diff --git a/tests/Unit/Cache/ReadOnlyFileDiscoverCacheDriverTest.php b/tests/Unit/Cache/ReadOnlyFileDiscoverCacheDriverTest.php
new file mode 100644
index 0000000..1d782a4
--- /dev/null
+++ b/tests/Unit/Cache/ReadOnlyFileDiscoverCacheDriverTest.php
@@ -0,0 +1,83 @@
+dir = sys_get_temp_dir() . '/tenup_ro_driver_' . uniqid( '', true );
+ mkdir( $this->dir );
+ }
+);
+
+afterEach(
+ function (): void {
+ if ( ! is_dir( $this->dir ) ) {
+ return;
+ }
+
+ $files = glob( $this->dir . '/*' );
+
+ if ( false !== $files ) {
+ array_map( 'unlink', $files );
+ }
+
+ rmdir( $this->dir );
+ }
+);
+
+it(
+ 'does not create the cache directory in the constructor',
+ function (): void {
+ $missing = sys_get_temp_dir() . '/tenup_ro_missing_' . uniqid( '', true );
+
+ new ReadOnlyFileDiscoverCacheDriver( $missing, false, 'cache.php' );
+
+ expect( is_dir( $missing ) )->toBeFalse();
+ }
+);
+
+it(
+ 'writes nothing when put() is called',
+ function (): void {
+ $driver = new ReadOnlyFileDiscoverCacheDriver( $this->dir, false, 'cache.php' );
+
+ $driver->put( 'id', [ 'Foo\\Bar' ] );
+
+ expect( $driver->has( 'id' ) )->toBeFalse();
+ expect( file_exists( $this->dir . '/cache.php' ) )->toBeFalse();
+ }
+);
+
+it(
+ 'deletes nothing when forget() is called',
+ function (): void {
+ file_put_contents( $this->dir . '/cache.php', 'dir, false, 'cache.php' );
+ $driver->forget( 'id' );
+
+ expect( file_exists( $this->dir . '/cache.php' ) )->toBeTrue();
+ }
+);
+
+it(
+ 'reads an existing cache file written with the generator settings',
+ function (): void {
+ // serialize = false and an explicit filename, matching generate_cache().
+ file_put_contents( $this->dir . '/cache.php', "dir, false, 'cache.php' );
+
+ expect( $driver->has( 'id' ) )->toBeTrue();
+ expect( $driver->get( 'id' ) )->toBe( [ 'Foo\\Bar' ] );
+ }
+);
diff --git a/tests/Unit/ModuleInitialization/ClassCacheTest.php b/tests/Unit/ModuleInitialization/ClassCacheTest.php
new file mode 100644
index 0000000..f9ca4e5
--- /dev/null
+++ b/tests/Unit/ModuleInitialization/ClassCacheTest.php
@@ -0,0 +1,119 @@
+dir = tenup_temp_class_dir();
+ }
+);
+
+afterEach(
+ function (): void {
+ tenup_remove_dir( $this->dir );
+ }
+);
+
+it(
+ 'writes a readable cache file and returns the discovered classes',
+ function (): void {
+ $cached = ModuleInitialization::instance()->generate_cache( $this->dir );
+
+ expect( file_exists( tenup_cache_file_path( $this->dir ) ) )->toBeTrue();
+ expect( $cached )->toContain( 'TenupTmp\\Widget' );
+ }
+);
+
+it(
+ 'reads the cache file when one is present',
+ function (): void {
+ $module_init = ModuleInitialization::instance();
+ $module_init->generate_cache( $this->dir );
+
+ // Tamper with the cache so a sentinel proves the read path used it rather than rescanning.
+ file_put_contents( tenup_cache_file_path( $this->dir ), "get_classes( $this->dir ) ) )->toBe( [ 'TenupTmp\\Sentinel' ] );
+ }
+);
+
+it(
+ 'writes no cache when none exists',
+ function (): void {
+ $classes = ModuleInitialization::instance()->get_classes( $this->dir );
+
+ expect( $classes )->toContain( 'TenupTmp\\Widget' );
+ // The whole point of the read-only runtime: a server can never create a cache it must later invalidate.
+ expect( is_dir( $this->dir . '/' . ModuleInitialization::CACHE_DIR_NAME ) )->toBeFalse();
+ }
+);
+
+it(
+ 'ignores a cache written by an older framework version',
+ function (): void {
+ $cache_dir = $this->dir . '/' . ModuleInitialization::CACHE_DIR_NAME;
+ mkdir( $cache_dir );
+
+ // 1.x wrote `discoverer-cache-{id}` as a serialized file under a different name.
+ file_put_contents( $cache_dir . '/discoverer-cache-TenupFramework', serialize( [ 'TenupTmp\\Legacy' ] ) );
+
+ $read = ModuleInitialization::instance()->get_classes( $this->dir );
+
+ expect( $read )->not->toContain( 'TenupTmp\\Legacy' );
+ expect( $read )->toContain( 'TenupTmp\\Widget' );
+ }
+);
+
+it(
+ 'falls back to a live scan when the cache is corrupt',
+ function ( string $contents ): void {
+ $cache_dir = $this->dir . '/' . ModuleInitialization::CACHE_DIR_NAME;
+
+ if ( ! is_dir( $cache_dir ) ) {
+ mkdir( $cache_dir );
+ }
+
+ // require() on this raises a ParseError, which the read path must catch rather than fatal.
+ file_put_contents( tenup_cache_file_path( $this->dir ), $contents );
+
+ expect( ModuleInitialization::instance()->get_classes( $this->dir ) )->toContain( 'TenupTmp\\Widget' );
+ }
+)->with(
+ [
+ 'unterminated array' => [ " [ 'generate_cache( $this->dir );
+
+ file_put_contents( tenup_cache_file_path( $this->dir ), "discover_live( $this->dir );
+
+ expect( $live )->toContain( 'TenupTmp\\Widget' );
+ expect( $live )->not->toContain( 'TenupTmp\\Sentinel' );
+ }
+);
+
+it(
+ 'throws when the directory does not exist',
+ function (): void {
+ ModuleInitialization::instance()->get_classes( $this->dir . '/does-not-exist-1234567' );
+ }
+)->throws( RuntimeException::class );
diff --git a/tests/Unit/ModuleInitialization/DisableCacheConstantTest.php b/tests/Unit/ModuleInitialization/DisableCacheConstantTest.php
new file mode 100644
index 0000000..89243bc
--- /dev/null
+++ b/tests/Unit/ModuleInitialization/DisableCacheConstantTest.php
@@ -0,0 +1,33 @@
+toBe( 0, $result['stderr'] );
+
+ $classes = json_decode( $result['stdout'], true, 512, JSON_THROW_ON_ERROR );
+
+ // The tampered cache's sentinel proves whether the cache was read.
+ expect( $classes )->not->toContain( 'TenupTmp\\Sentinel' );
+ expect( $classes )->toContain( 'TenupTmp\\Widget' );
+
+ tenup_remove_dir( $dir );
+ }
+);
diff --git a/tests/Unit/SmokeTest.php b/tests/Unit/SmokeTest.php
new file mode 100644
index 0000000..ed6d150
--- /dev/null
+++ b/tests/Unit/SmokeTest.php
@@ -0,0 +1,15 @@
+toBeTrue();
+ }
+);
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 31479ab..baf6010 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,12 +1,77 @@
sys_get_temp_dir() . '/wp-framework-tests-wordpress',
+ 'WP_DB_NAME' => TENUP_FRAMEWORK_TEST_DB,
+ 'WP_DB_USER' => 'root',
+ 'WP_DB_PASSWORD' => '',
+ 'WP_DB_HOST' => '127.0.0.1',
+];
+
+foreach ( $tenup_framework_env_defaults as $tenup_framework_variable => $tenup_framework_default ) {
+ if ( false === getenv( $tenup_framework_variable ) ) {
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv
+ putenv( "{$tenup_framework_variable}={$tenup_framework_default}" );
+ }
+}
+
+unset( $tenup_framework_env_defaults, $tenup_framework_variable, $tenup_framework_default );
+
+/*
+ * Refuse to run against a database we were not pointed at. The installer is destructive, so
+ * a config that has drifted (a stale cached file, a stray WP_DB_NAME) must stop the suite
+ * rather than quietly rebuild someone else's tables.
+ */
+$tenup_framework_config = getenv( 'WP_CORE_DIR' ) . '/wp-tests-config.php';
+
+if ( is_readable( $tenup_framework_config ) ) {
+ $tenup_framework_contents = (string) file_get_contents( $tenup_framework_config ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+ $tenup_framework_expected = getenv( 'WP_DB_NAME' );
+
+ if ( ! str_contains( $tenup_framework_contents, "'DB_NAME', '{$tenup_framework_expected}'" ) ) {
+ fwrite(
+ STDERR,
+ PHP_EOL . 'Refusing to run: ' . $tenup_framework_config
+ . ' does not target the expected test database (' . $tenup_framework_expected . ').'
+ . PHP_EOL . 'Delete that file and re-run so it is regenerated.' . PHP_EOL
+ );
+ exit( 1 );
+ }
+
+ unset( $tenup_framework_contents, $tenup_framework_expected );
+}
-namespace TenupFrameworkTests;
+unset( $tenup_framework_config );
-require dirname( __DIR__, 1 ) . '/vendor/autoload.php';
+\Mantle\Testing\install();
diff --git a/tests/scripts/disable-class-cache.php b/tests/scripts/disable-class-cache.php
new file mode 100644
index 0000000..798ba21
--- /dev/null
+++ b/tests/scripts/disable-class-cache.php
@@ -0,0 +1,39 @@
+
+ * Prints the discovered class list as JSON.
+ *
+ * @package TenupFramework
+ */
+
+declare( strict_types = 1 );
+
+use TenupFramework\ModuleInitialization;
+
+require dirname( __DIR__, 2 ) . '/vendor/autoload.php';
+
+if ( ! isset( $argv[1] ) ) {
+ fwrite( STDERR, 'Usage: disable-class-cache.php ' . PHP_EOL );
+ exit( 1 );
+}
+
+$dir = $argv[1];
+$module_init = ModuleInitialization::instance();
+
+// Build a real cache, then tamper with it: the sentinel is what a cache read would return.
+$module_init->generate_cache( $dir );
+
+file_put_contents(
+ $dir . '/' . ModuleInitialization::CACHE_DIR_NAME . '/' . ModuleInitialization::CACHE_FILENAME,
+ "get_classes( $dir ) ) );