diff --git a/.distignore b/.distignore
index 3407609..8257854 100644
--- a/.distignore
+++ b/.distignore
@@ -12,8 +12,8 @@ coverage
.editorconfig
.eslintrc.js
.prettierrc
-phpunit.xml
-phpcs.xml
+phpunit.xml*
+phpcs.xml*
phpstan.neon
# Build tools
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
new file mode 100644
index 0000000..4753a35
--- /dev/null
+++ b/.github/workflows/quality.yml
@@ -0,0 +1,74 @@
+name: Quality
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ php-syntax:
+ name: PHP ${{ matrix.php }} syntax
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
+ steps:
+ - uses: actions/checkout@v4
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none
+ - run: php tests/php-lint.php
+
+ quality:
+ name: Coding standards and tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ coverage: none
+ tools: composer:v2
+ - run: composer install --no-interaction --prefer-dist
+ - run: composer phpcs
+ - run: composer test
+ - run: |
+ for file in $(find assets -name '*.js' -type f); do
+ node --check "$file"
+ done
+ - run: bash -n build.sh
+
+ package:
+ name: Distribution package
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - run: ./build.sh
+ - run: |
+ version=$(grep -m1 'Version:' functionalities.php | awk '{print $NF}')
+ zip="functionalities-${version}.zip"
+ test -s "$zip"
+ unzip -t "$zip"
+ for required in \
+ functionalities/functionalities.php \
+ functionalities/readme.txt \
+ functionalities/uninstall.php \
+ functionalities/includes/admin/class-admin.php \
+ functionalities/includes/admin/class-module-controller.php \
+ functionalities/includes/admin/class-settings-portability-controller.php \
+ functionalities/includes/admin/class-site-health-controller.php \
+ functionalities/includes/core/class-module-registry.php \
+ functionalities/includes/storage/class-atomic-json-store.php \
+ functionalities/assets/js/admin-redirects.js \
+ functionalities/assets/js/admin-tools.js; do
+ unzip -Z1 "$zip" | grep -qx "$required"
+ done
+ if unzip -Z1 "$zip" | grep -Eq '(^|/)(tests|vendor|node_modules|\.git|\.github)/|composer\.(json|lock)$|\.md$'; then
+ echo 'Development files leaked into the distribution.' >&2
+ exit 1
+ fi
diff --git a/README.md b/README.md
index 348e262..5bee44d 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Dynamic Functionalities
-All-in-one WordPress optimization toolkit with 15+ modules for performance, security, SEO, and content management. Built with modern WordPress coding standards and a clean module-based dashboard. Optimized for performance with lazy-loading, static property caching, and intelligent transients.
+All-in-one WordPress optimization toolkit with 16 modules for performance, security, SEO, and content management. Built with modern WordPress coding standards and a clean module-based dashboard. Optimized for performance with modular initialization, static property caching, and intelligent transients.
-**Version:** 1.4.7
+**Version:** 1.4.8
**License:** GPL-2.0-or-later
**Text Domain:** `functionalities`
**Pricing:** Free
@@ -19,18 +19,8 @@ All-in-one WordPress optimization toolkit with 15+ modules for performance, secu
All modules are accessed through a unified dashboard at `wp-admin/admin.php?page=functionalities`. Click any module card to configure its settings.
Learn more on [Functionalities Site](https://functionalities.dev)
----
-
-## Performance & Footprint
-
-This plugin is built with a "Performance First" philosophy. Unlike many all-in-one plugins that slow down your site, Functionalities is designed to be as lightweight as possible:
-- **Modular & Lazy Loaded:** Using a custom autoloader, the plugin only loads the code required for active modules. If a feature is disabled, its code is never even included in memory.
-- **Minimized Database Load:** All module settings are cached in static properties. This ensures that `get_option()` is called at most once per module per request, regardless of how many times a feature is accessed.
-- **Zero Frontend Bloat:** Most modules are "Zero Footprint" on the frontend, meaning they load no CSS or JS unless explicitly required (like the Components or Fonts modules).
-- **Intelligent Filtering:** Content filters (`the_content`, etc.) use `strpos()` fast-exit checks. If the specific markers or tags for a feature aren't present in your content, the plugin exits immediately without running expensive regular expressions or DOM parsing.
-- **Efficient HTML Processing:** We use targeted regex for lightweight tasks (like Schema injection) and only resort to `DOMDocument` when structural manipulation is strictly necessary, ensuring maximum speed.
-- **Aggressive Caching:** Heavy operations—such as reading JSON exception lists, calculating file hashes, or managing redirects—are cached using WordPress Transients or versioned options to minimize Disk I/O.
+See the [public roadmap](ROADMAP.md) for planned fixes and features.
---
@@ -38,7 +28,7 @@ This plugin is built with a "Performance First" philosophy. Unlike many all-in-o
This plugin is built with a "Performance First" philosophy. Unlike many all-in-one plugins that slow down your site, Functionalities is designed to be as lightweight as possible:
-- **Modular & Lazy Loaded:** Using a custom autoloader, the plugin only loads the code required for active modules. If a feature is disabled, its code is never even included in memory.
+- **Modular Initialization:** Each module checks its enabled state before registering its feature hooks. Disabled modules add no frontend assets or feature behavior.
- **Minimized Database Load:** All module settings are cached in static properties. This ensures that `get_option()` is called at most once per module per request, regardless of how many times a feature is accessed.
- **Zero Frontend Bloat:** Most modules are "Zero Footprint" on the frontend, meaning they load no CSS or JS unless explicitly required (like the Components or Fonts modules).
- **Intelligent Filtering:** Content filters (`the_content`, etc.) use `strpos()` fast-exit checks. If the specific markers or tags for a feature aren't present in your content, the plugin exits immediately without running expensive regular expressions or DOM parsing.
@@ -254,6 +244,8 @@ Manage URL redirects directly from WordPress admin with high-performance file-ba
- File-based JSON storage for zero database overhead during redirects
- Integrated hit counter for tracking redirect usage
- Normalized path matching
+- CSV import/export with a validated dry-run preview
+- Optional bounded 404 aggregation without visitor identifiers or full referrers
**Navigate to:** `?page=functionalities&module=redirect-manager`
@@ -274,6 +266,21 @@ Enhanced login protection and security measures for your WordPress site.
---
+### Progressive Web App
+
+Make the site installable with a web app manifest, service worker, offline fallback, and optional install prompt.
+
+**Features:**
+- Configurable app name, colors, icons, display mode, and orientation
+- Offline page and versioned runtime caching
+- App shortcuts, screenshots, and advanced manifest fields
+- Optional install prompt and Web Share Target support
+- Root-level manifest and service worker endpoints
+
+**Navigate to:** `?page=functionalities&module=pwa`
+
+---
+
## Link Management: Developer Reference
### JSON Preset Format
@@ -373,6 +380,8 @@ functionalities/
│ │ └── svg-icons-editor.css
│ └── js/
│ ├── admin.js
+│ ├── admin-redirects.js
+│ ├── admin-tools.js
│ ├── admin-ui.js
│ ├── content-regression.js
│ └── svg-icons-editor.js
@@ -380,7 +389,15 @@ functionalities/
│ ├── admin/
│ │ ├── class-admin.php
│ │ ├── class-admin-ui.php
-│ │ └── class-module-docs.php
+│ │ ├── class-module-controller.php
+│ │ ├── class-module-docs.php
+│ │ ├── class-redirect-manager-controller.php
+│ │ ├── class-settings-portability-controller.php
+│ │ ├── class-site-health-controller.php
+│ │ ├── class-svg-icons-controller.php
+│ │ └── class-task-manager-controller.php
+│ ├── core/
+│ │ └── class-module-registry.php
│ ├── features/
│ │ ├── class-assumption-detection.php
│ │ ├── class-block-cleanup.php
@@ -392,11 +409,14 @@ functionalities/
│ │ ├── class-login-security.php
│ │ ├── class-meta.php
│ │ ├── class-misc.php
+│ │ ├── class-pwa.php
│ │ ├── class-redirect-manager.php
│ │ ├── class-schema.php
│ │ ├── class-snippets.php
│ │ ├── class-svg-icons.php
│ │ └── class-task-manager.php
+│ └── storage/
+│ └── class-atomic-json-store.php
├── languages/
├── exception-urls.json.sample
├── functionalities.php
@@ -409,9 +429,23 @@ functionalities/
## Adding New Modules
1. Create a feature class in `includes/features/class-your-module.php`
-2. Add module definition in `Admin::define_modules()`
-3. Register settings in `Admin::register_settings()`
-4. Initialize in `functionalities.php`
+2. Add its definition to `Core\Module_Registry::get_definitions()`
+3. Register its settings in the module controller
+4. Add focused tests for defaults and any pure helpers
+
+## Local development checks
+
+```bash
+composer install
+composer lint
+composer phpcs
+composer test
+node --check assets/js/admin.js
+bash -n build.sh
+./build.sh
+```
+
+The pull-request workflow runs PHP syntax checks on PHP 7.4 through 8.5, coding standards, PHPUnit, JavaScript and shell syntax, version consistency, and distribution ZIP assertions.
Example module definition:
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..5b1e706
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,57 @@
+# Dynamic Functionalities Roadmap
+
+All planned fixes and features below ship in **v1.4.8**. GitHub issues remain the
+source of truth for scope and acceptance criteria. The release is tagged only
+after automated checks and the real-WordPress smoke gate pass.
+
+## Phase 1: Stabilize the foundation
+
+- Fix SVG Icons so a fresh install leaves the module disabled until the user
+ enables it, matching the explicit-activation policy introduced in v1.2.0.
+- Correct README duplication, module coverage, and performance claims.
+- Make Task Manager and Redirect Manager JSON updates atomic and concurrency-safe
+ ([#41](https://github.com/wpgaurav/functionalities/issues/41)).
+- Add pull-request CI, supported-PHP checks, distribution validation, coding
+ standards, and a minimal test foundation
+ ([#42](https://github.com/wpgaurav/functionalities/issues/42)).
+
+## Phase 2: Improve architecture and portability
+
+- Introduce a true lazy module registry so disabled feature classes are not
+ loaded on frontend requests
+ ([#43](https://github.com/wpgaurav/functionalities/issues/43)).
+- Split the monolithic Admin class into module controllers and move remaining
+ inline assets into versioned files
+ ([#44](https://github.com/wpgaurav/functionalities/issues/44)).
+- Add validated, versioned settings export/import plus a redacted diagnostics
+ bundle ([#45](https://github.com/wpgaurav/functionalities/issues/45)).
+
+## Phase 3: Add operational workflows
+
+- Add CSV redirect migration and a bounded, privacy-conscious 404 monitor
+ ([#46](https://github.com/wpgaurav/functionalities/issues/46)).
+- Integrate Assumption Detection and Content Integrity with Site Health,
+ scheduled scans, deduplicated notifications, and useful snapshot diffs
+ ([#47](https://github.com/wpgaurav/functionalities/issues/47)).
+
+## v1.4.8 release gate
+
+- All seven roadmap issues are closed through reviewed changes.
+- Automated checks pass across the supported PHP and WordPress range.
+- Concurrent file updates cannot lose redirects, tasks, or hit counts.
+- Existing settings, hooks, admin URLs, and stored data remain compatible.
+- Settings can round-trip between clean sites without exposing sensitive data.
+- Monitoring is opt-in, capped, rate-limited, and cheap on frontend requests.
+- Redirect imports are validated, previewable, and all-or-nothing.
+- The manual block-editor iframe smoke test in `CLAUDE.md` passes in a real
+ WordPress install.
+
+## Planning rules
+
+- Fix security, data loss, and compatibility regressions before feature work.
+- Every feature needs a disabled-by-default path with no frontend assets.
+- Persisted-data changes need migration, rollback, concurrency, and uninstall
+ coverage.
+- New admin UI must reuse WordPress patterns and remain keyboard accessible.
+- A release is not complete until the GitHub artifact, WordPress.org package,
+ version metadata, and manual editor smoke test agree.
diff --git a/assets/css/admin.css b/assets/css/admin.css
index 84afb28..c398a37 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -1543,3 +1543,42 @@
grid-template-columns: repeat(2, 1fr);
}
}
+
+.functionalities-tools {
+ margin-top: 30px;
+ padding: 20px;
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ border-radius: 4px;
+}
+
+.functionalities-tools h2 {
+ margin-top: 0;
+}
+
+.functionalities-tools details {
+ margin: 14px 0;
+}
+
+.functionalities-tools__modules {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
+ gap: 8px 16px;
+ margin: 12px 0;
+}
+
+.functionalities-tools__actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 16px;
+}
+
+.functionalities-tools__result {
+ max-height: 320px;
+ overflow: auto;
+ margin: 16px 0 0;
+ padding: 12px;
+ background: #f6f7f7;
+ white-space: pre-wrap;
+}
diff --git a/assets/js/admin-redirects.js b/assets/js/admin-redirects.js
new file mode 100644
index 0000000..e43d4ff
--- /dev/null
+++ b/assets/js/admin-redirects.js
@@ -0,0 +1,104 @@
+(function ($) {
+ 'use strict';
+
+ if (typeof functionalitiesRedirectAdmin === 'undefined') {
+ return;
+ }
+
+ var config = functionalitiesRedirectAdmin;
+
+ function message(response) {
+ return response && response.data && response.data.message ? response.data.message : 'Error';
+ }
+
+ function download(filename, content, type) {
+ var link = document.createElement('a');
+ link.href = URL.createObjectURL(new Blob([content], { type: type }));
+ link.download = filename;
+ link.click();
+ URL.revokeObjectURL(link.href);
+ }
+
+ $(function () {
+ $('#redirect-search').on('input', function () {
+ var term = $(this).val().toLowerCase();
+ $('#redirects-list tr:not(.no-items)').each(function () {
+ var row = $(this);
+ var matches = row.find('td:eq(1)').text().toLowerCase().indexOf(term) !== -1 || row.find('td:eq(2)').text().toLowerCase().indexOf(term) !== -1;
+ row.toggle(matches);
+ });
+ });
+
+ $('#add-redirect-btn').on('click', function () {
+ var from = $('#redirect-from').val().trim();
+ var to = $('#redirect-to').val().trim();
+ if (!from || !to) {
+ window.alert(config.bothRequired);
+ return;
+ }
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_add', nonce: config.nonce, from: from, to: to, type: $('#redirect-type').val() }, function (response) {
+ if (response.success) { window.location.reload(); } else { window.alert(message(response)); }
+ });
+ });
+
+ $(document).on('change', '.toggle-redirect', function () {
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_toggle', nonce: config.nonce, id: $(this).closest('tr').data('id') });
+ });
+
+ $(document).on('click', '.delete-redirect', function () {
+ if (!window.confirm(config.deletePrompt)) { return; }
+ var row = $(this).closest('tr');
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_delete', nonce: config.nonce, id: row.data('id') }, function (response) {
+ if (response.success) { row.fadeOut(function () { row.remove(); }); }
+ });
+ });
+
+ $('#export-redirects-btn, #export-redirects-csv-btn').on('click', function () {
+ var format = this.id.indexOf('csv') !== -1 ? 'csv' : 'json';
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_export', nonce: config.nonce, format: format }, function (response) {
+ if (response.success) { download(response.data.filename, response.data.content, format === 'csv' ? 'text/csv' : 'application/json'); }
+ });
+ });
+
+ $('#import-redirects-btn, #import-redirects-csv-btn').on('click', function () {
+ $('#import-format').val(this.id.indexOf('csv') !== -1 ? 'csv' : 'json');
+ $('#import-preview').prop('hidden', true).text('');
+ $('#confirm-import').prop('disabled', true);
+ $('#import-modal').css('display', 'flex');
+ });
+ $('#cancel-import').on('click', function () { $('#import-modal').hide(); });
+
+ $('#preview-import').on('click', function () {
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_import', nonce: config.nonce, document: $('#import-json').val(), format: $('#import-format').val(), dry_run: 1 }, function (response) {
+ $('#import-preview').prop('hidden', false).text(JSON.stringify(response.data, null, 2));
+ $('#confirm-import').prop('disabled', !response.success);
+ });
+ });
+
+ $('#confirm-import').on('click', function () {
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_import', nonce: config.nonce, document: $('#import-json').val(), format: $('#import-format').val() }, function (response) {
+ if (response.success) { window.location.reload(); } else { window.alert(message(response)); }
+ });
+ });
+
+ $('#purge-404-btn').on('click', function () {
+ if (!window.confirm(config.purgePrompt)) { return; }
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_404_purge', nonce: config.nonce }, function (response) {
+ if (response.success) { window.location.reload(); }
+ });
+ });
+
+ $(document).on('click', '.ignore-404', function () {
+ var row = $(this).closest('tr');
+ $.post(config.ajaxUrl, { action: 'functionalities_redirect_404_ignore', nonce: config.nonce, path: row.data('path') }, function (response) {
+ if (response.success) { row.remove(); }
+ });
+ });
+
+ $(document).on('click', '.create-from-404', function () {
+ $('#redirect-from').val($(this).closest('tr').data('path'));
+ $('#redirect-to').trigger('focus');
+ window.scrollTo({ top: $('#redirect-from').offset().top - 80, behavior: 'smooth' });
+ });
+ });
+}(jQuery));
diff --git a/assets/js/admin-tools.js b/assets/js/admin-tools.js
new file mode 100644
index 0000000..d528c55
--- /dev/null
+++ b/assets/js/admin-tools.js
@@ -0,0 +1,78 @@
+(function () {
+ 'use strict';
+
+ var root = document.querySelector('[data-functionalities-tools]');
+ if (!root || typeof functionalitiesTools === 'undefined') {
+ return;
+ }
+
+ var fileInput = root.querySelector('[data-tools-file]');
+ var previewButton = root.querySelector('[data-tools-preview]');
+ var applyButton = root.querySelector('[data-tools-apply]');
+ var result = root.querySelector('[data-tools-result]');
+ var documentText = '';
+
+ function request(action, data) {
+ var body = new FormData();
+ body.append('action', action);
+ body.append('nonce', functionalitiesTools.nonce);
+ Object.keys(data || {}).forEach(function (key) {
+ var value = data[key];
+ if (Array.isArray(value)) {
+ value.forEach(function (item) { body.append(key + '[]', item); });
+ } else {
+ body.append(key, value);
+ }
+ });
+ return fetch(functionalitiesTools.ajaxUrl, { method: 'POST', credentials: 'same-origin', body: body }).then(function (response) { return response.json(); });
+ }
+
+ function show(data) {
+ result.hidden = false;
+ result.textContent = JSON.stringify(data, null, 2);
+ }
+
+ function download(filename, content) {
+ var link = document.createElement('a');
+ link.href = URL.createObjectURL(new Blob([content], { type: 'application/json' }));
+ link.download = filename;
+ link.click();
+ URL.revokeObjectURL(link.href);
+ }
+
+ root.querySelector('[data-tools-export]').addEventListener('click', function () {
+ var modules = Array.prototype.map.call(root.querySelectorAll('[data-tools-module]:checked'), function (input) { return input.value; });
+ request('functionalities_settings_export', { modules: modules, include_code: root.querySelector('[data-tools-include-code]').checked ? '1' : '' }).then(function (response) {
+ if (response.success) { download(response.data.filename, response.data.content); } else { show(response.data); }
+ });
+ });
+
+ fileInput.addEventListener('change', function () {
+ if (!fileInput.files.length) { return; }
+ fileInput.files[0].text().then(function (text) {
+ documentText = text;
+ previewButton.disabled = false;
+ applyButton.disabled = true;
+ });
+ });
+
+ previewButton.addEventListener('click', function () {
+ request('functionalities_settings_preview', { document: documentText, include_code: root.querySelector('[data-tools-include-code]').checked ? '1' : '' }).then(function (response) {
+ show(response.data);
+ applyButton.disabled = !response.success;
+ });
+ });
+
+ applyButton.addEventListener('click', function () {
+ request('functionalities_settings_import', { document: documentText, include_code: root.querySelector('[data-tools-include-code]').checked ? '1' : '' }).then(function (response) {
+ show(response.data);
+ applyButton.disabled = response.success;
+ });
+ });
+
+ root.querySelector('[data-tools-diagnostics]').addEventListener('click', function () {
+ request('functionalities_diagnostics').then(function (response) {
+ if (response.success) { download(response.data.filename, response.data.content); } else { show(response.data); }
+ });
+ });
+}());
diff --git a/assets/js/content-regression.js b/assets/js/content-regression.js
index 61d8f7a..4b6b372 100644
--- a/assets/js/content-regression.js
+++ b/assets/js/content-regression.js
@@ -276,6 +276,28 @@
);
}
+ /**
+ * Explain heading-level additions and removals from the snapshot diff.
+ *
+ * @param {Object} props Component props.
+ * @return {JSX.Element|null} Heading change details.
+ */
+ function HeadingChanges( { diff } ) {
+ if ( ! diff || ( ! diff.headings_added.length && ! diff.headings_removed.length ) ) {
+ return null;
+ }
+
+ const added = diff.headings_added.map( ( level ) => 'H' + level ).join( ', ' );
+ const removed = diff.headings_removed.map( ( level ) => 'H' + level ).join( ', ' );
+
+ return wp.element.createElement(
+ 'div',
+ { style: { marginTop: '10px', fontSize: '12px', color: '#50575e' } },
+ added && wp.element.createElement( 'p', { style: { margin: '0 0 4px' } }, i18n.headingsAdded + ' ' + added ),
+ removed && wp.element.createElement( 'p', { style: { margin: 0 } }, i18n.headingsRemoved + ' ' + removed )
+ );
+ }
+
/**
* Main regression panel content component.
*
@@ -411,7 +433,7 @@
return wp.element.createElement( NoBaseline );
}
- const { warnings, has_baseline: hasBaseline, post_settings: postSettings, current, baseline } = status;
+ const { warnings, has_baseline: hasBaseline, post_settings: postSettings, current, baseline, diff } = status;
const hasWarnings = warnings && warnings.length > 0;
return wp.element.createElement(
@@ -439,6 +461,7 @@
current: current,
baseline: baseline
} ),
+ hasBaseline && showActions && wp.element.createElement( HeadingChanges, { diff: diff } ),
// Actions (only shown if there are warnings and showActions is true).
showActions && hasWarnings && ! ignoreWarnings && wp.element.createElement(
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..c4786d2
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "wpgaurav/functionalities",
+ "description": "Development tooling for the Dynamic Functionalities WordPress plugin.",
+ "type": "wordpress-plugin",
+ "license": "GPL-2.0-or-later",
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.1",
+ "phpcompatibility/phpcompatibility-wp": "^2.1.8",
+ "phpunit/phpunit": "^9.6.31",
+ "wp-coding-standards/wpcs": "^3.4"
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ },
+ "platform": {
+ "php": "7.4.0"
+ },
+ "sort-packages": true
+ },
+ "scripts": {
+ "lint": "@php tests/php-lint.php",
+ "phpcs": "@php -d error_reporting=8191 vendor/bin/phpcs",
+ "test": "phpunit",
+ "check": [
+ "@lint",
+ "@phpcs",
+ "@test"
+ ]
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..9b088b1
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,2431 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "61727f05b3b9a4d20249821dbdb1a511",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.1.0 || ^4.0"
+ },
+ "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"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "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",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "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"
+ },
+ "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-05-06T08:26:05+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
+ "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^11",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.30 || ^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
+ },
+ "funding": [
+ {
+ "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%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-30T00:15:36+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": "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": "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": "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.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
+ "reference": "b598aa890815b8df16363271b659d73280129101"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101",
+ "reference": "b598aa890815b8df16363271b659d73280129101",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "phpcsstandards/phpcsutils": "^1.2.0",
+ "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": "2025-11-12T23:06:57+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsutils",
+ "version": "1.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
+ "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55",
+ "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55",
+ "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": "2025-12-08T14:27:58+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.32",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5",
+ "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5",
+ "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"
+ },
+ "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"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "9.2.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/9.2.32"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-22T04:23:01+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "3.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.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",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-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",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:58:55+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:16:10+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "9.6.35",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0edba2f3a0c48df3553cb9b640810b30df60302b",
+ "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b",
+ "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"
+ },
+ "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"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.6-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/9.6.35"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsoring.html",
+ "type": "other"
+ }
+ ],
+ "time": "2026-07-06T14:48:07+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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 parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T06:27:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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 PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:08:54+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:30:19+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "4.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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": "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",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10"
+ },
+ "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-01-24T09:22:56+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-22T06:19:30+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T06:30:58+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "5.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.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": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:03:51+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "4.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.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/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:03:27+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "5.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.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/global-state",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T07:10:35+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-22T06:20:34+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:12:34+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:14:26+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0",
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6"
+ },
+ "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-10T06:57:39+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "3.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-14T16:00:52+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.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": "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",
+ "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:13:03+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.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",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-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": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/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"
+ ],
+ "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"
+ },
+ "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": "2025-11-04T16:30:35+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "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"
+ }
+ ],
+ "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"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-17T20:03:58+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
+ "reference": "469c18ceab4d642b15bad4c65ebf3b307bfd55ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/469c18ceab4d642b15bad4c65ebf3b307bfd55ab",
+ "reference": "469c18ceab4d642b15bad4c65ebf3b307bfd55ab",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "ext-libxml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlreader": "*",
+ "php": ">=7.2",
+ "phpcsstandards/phpcsextra": "^1.5.0",
+ "phpcsstandards/phpcsutils": "^1.2.2",
+ "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/WordPress/WordPress-Coding-Standards/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis",
+ "wordpress"
+ ],
+ "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"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "custom"
+ }
+ ],
+ "time": "2026-07-16T13:05:29+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {},
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "7.4.0"
+ },
+ "plugin-api-version": "2.9.0"
+}
diff --git a/docs/performance-baseline.md b/docs/performance-baseline.md
new file mode 100644
index 0000000..4eb04c8
--- /dev/null
+++ b/docs/performance-baseline.md
@@ -0,0 +1,22 @@
+# Module loading baseline
+
+The v1.4.8 registry reads each module's lightweight option before referencing its feature class.
+
+| Frontend request | v1.4.7 feature files | v1.4.8 feature files |
+| --- | ---: | ---: |
+| All modules disabled | 16 | 0 |
+| Only Performance & Cleanup enabled | 16 | 1 |
+
+The isolated registry test records included files for both v1.4.8 scenarios. Shared bootstrap and storage classes remain autoloaded only when their callers need them. Existing option names, enable filters, admin URLs, and module classes remain compatible.
+
+## WordPress runtime measurement
+
+The all-disabled scenario was also measured on July 22, 2026 in the same local WordPress Studio site on PHP 8.3. Each result was reproduced across three requests after swapping only the plugin version.
+
+| Version | Feature files | Memory usage | Peak memory | Database queries |
+| --- | ---: | ---: | ---: | ---: |
+| v1.4.7 | 16 | 59,171,832 bytes | 59,361,480 bytes | 4 |
+| v1.4.8 | 0 | 56,842,472 bytes | 57,032,464 bytes | 4 |
+| Difference | -16 | -2,329,360 bytes | -2,329,016 bytes | 0 |
+
+With all modules disabled, v1.4.8 avoids loading every feature implementation and reduces request memory by about 2.22 MiB in this environment. The database query count is unchanged because WordPress loads these small module flags from its autoloaded options cache.
diff --git a/functionalities.php b/functionalities.php
index 9f132ae..c1ef07c 100644
--- a/functionalities.php
+++ b/functionalities.php
@@ -3,7 +3,7 @@
* Plugin Name: Dynamic Functionalities
* Plugin URI: https://functionalities.dev
* Description: All-in-one WordPress optimization toolkit. 15+ modules for performance, security, SEO, and content management.
- * Version: 1.4.7
+ * Version: 1.4.8
* Author: Gaurav Tiwari
* Author URI: https://gauravtiwari.org
* License: GPL-2.0-or-later
@@ -13,96 +13,110 @@
* Requires PHP: 7.4
*/
-if (!defined('ABSPATH')) {
+if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// Define constants.
-if (!defined('FUNCTIONALITIES_VERSION')) {
- define('FUNCTIONALITIES_VERSION', '1.4.7');
+if ( ! defined( 'FUNCTIONALITIES_VERSION' ) ) {
+ define( 'FUNCTIONALITIES_VERSION', '1.4.8' );
}
-if (!defined('FUNCTIONALITIES_FILE')) {
- define('FUNCTIONALITIES_FILE', __FILE__);
+if ( ! defined( 'FUNCTIONALITIES_FILE' ) ) {
+ define( 'FUNCTIONALITIES_FILE', __FILE__ );
}
-if (!defined('FUNCTIONALITIES_DIR')) {
- define('FUNCTIONALITIES_DIR', plugin_dir_path(__FILE__));
+if ( ! defined( 'FUNCTIONALITIES_DIR' ) ) {
+ define( 'FUNCTIONALITIES_DIR', plugin_dir_path( __FILE__ ) );
}
-if (!defined('FUNCTIONALITIES_URL')) {
- define('FUNCTIONALITIES_URL', plugin_dir_url(__FILE__));
+if ( ! defined( 'FUNCTIONALITIES_URL' ) ) {
+ define( 'FUNCTIONALITIES_URL', plugin_dir_url( __FILE__ ) );
}
// Simple autoloader for plugin classes.
-spl_autoload_register(function (string $class) {
- if (strpos($class, 'Functionalities\\') !== 0) {
- return;
- }
+spl_autoload_register(
+ function ( string $class ) {
+ if ( strpos( $class, 'Functionalities\\' ) !== 0 ) {
+ return;
+ }
- $parts = explode('\\', $class);
- array_shift($parts); // Remove Functionalities
+ $parts = explode( '\\', $class );
+ array_shift( $parts ); // Remove Functionalities
- $subpath = '';
- if (count($parts) > 1) {
- $subpath = strtolower((string) $parts[0]) . '/';
- }
+ $subpath = '';
+ if ( count( $parts ) > 1 ) {
+ $subpath = strtolower( (string) $parts[0] ) . '/';
+ }
- $basename = strtolower(str_replace('_', '-', (string) end($parts)));
+ $basename = strtolower( str_replace( '_', '-', (string) end( $parts ) ) );
- // Try class- prefix first, then trait- prefix.
- $file = FUNCTIONALITIES_DIR . 'includes/' . $subpath . 'class-' . $basename . '.php';
- if (!file_exists($file)) {
- $file = FUNCTIONALITIES_DIR . 'includes/' . $subpath . 'trait-' . $basename . '.php';
- }
+ // Try class- prefix first, then trait- prefix.
+ $file = FUNCTIONALITIES_DIR . 'includes/' . $subpath . 'class-' . $basename . '.php';
+ if ( ! file_exists( $file ) ) {
+ $file = FUNCTIONALITIES_DIR . 'includes/' . $subpath . 'trait-' . $basename . '.php';
+ }
- if (file_exists($file)) {
- require_once $file;
+ if ( file_exists( $file ) ) {
+ require_once $file;
+ }
}
-});
+);
-// Initialize plugin on init hook.
-\add_action('init', function () {
- \Functionalities\Admin\Admin::init();
- \Functionalities\Features\Link_Management::init();
- \Functionalities\Features\Block_Cleanup::init();
- \Functionalities\Features\Editor_Links::init();
- \Functionalities\Features\Misc::init();
- \Functionalities\Features\Snippets::init();
- \Functionalities\Features\Schema::init();
- \Functionalities\Features\Components::init();
- \Functionalities\Features\Fonts::init();
- \Functionalities\Features\Meta::init();
- \Functionalities\Features\Content_Regression::init();
- \Functionalities\Features\Assumption_Detection::init();
- \Functionalities\Features\Task_Manager::init();
- \Functionalities\Features\Redirect_Manager::init();
- \Functionalities\Features\Login_Security::init();
- \Functionalities\Features\SVG_Icons::init();
- \Functionalities\Features\PWA::init();
-}, 10);
+// Initialize admin and only the modules required for this request.
+\add_action(
+ 'plugins_loaded',
+ function () {
+ if ( \is_admin() ) {
+ \Functionalities\Admin\Admin::init();
+ }
+ \Functionalities\Admin\Site_Health_Controller::init();
+ \add_action( 'init', array( '\Functionalities\Core\Module_Registry', 'boot' ), 5 );
+ },
+ 10
+);
+
+\add_action( 'updated_option', array( '\Functionalities\Core\Module_Registry', 'handle_option_update' ), 10, 3 );
// Activation hook.
-\register_activation_hook(__FILE__, function () {
- if (function_exists('flush_rewrite_rules')) {
- \flush_rewrite_rules();
+\register_activation_hook(
+ __FILE__,
+ function () {
+ if ( \Functionalities\Core\Module_Registry::is_enabled( 'pwa' ) ) {
+ \Functionalities\Core\Module_Registry::boot_module( 'pwa' );
+ }
+ if ( function_exists( 'flush_rewrite_rules' ) ) {
+ \flush_rewrite_rules();
+ }
}
-});
+);
// Quick Settings link on the Plugins screen.
-\add_filter('plugin_action_links_' . \plugin_basename(__FILE__), function (array $links): array {
- $url = \admin_url('admin.php?page=functionalities');
- $links[] = '' . \esc_html__('Settings', 'functionalities') . '';
- return $links;
-});
+\add_filter(
+ 'plugin_action_links_' . \plugin_basename( __FILE__ ),
+ function ( array $links ): array {
+ $url = \admin_url( 'admin.php?page=functionalities' );
+ $links[] = '' . \esc_html__( 'Settings', 'functionalities' ) . '';
+ return $links;
+ }
+);
// Add meta links on the Plugins screen (row meta).
-\add_filter('plugin_row_meta', function (array $links, string $file): array {
- if (\plugin_basename(__FILE__) === $file) {
- $links[] = '' . \esc_html__('Support', 'functionalities') . '';
- $links[] = '' . \esc_html__('Report Issues', 'functionalities') . '';
- }
- return $links;
-}, 10, 2);
+\add_filter(
+ 'plugin_row_meta',
+ function ( array $links, string $file ): array {
+ if ( \plugin_basename( __FILE__ ) === $file ) {
+ $links[] = '' . \esc_html__( 'Support', 'functionalities' ) . '';
+ $links[] = '' . \esc_html__( 'Report Issues', 'functionalities' ) . '';
+ }
+ return $links;
+ },
+ 10,
+ 2
+);
-\register_deactivation_hook(__FILE__, function () {
- if (function_exists('flush_rewrite_rules')) {
- \flush_rewrite_rules();
+\register_deactivation_hook(
+ __FILE__,
+ function () {
+ \wp_clear_scheduled_hook( 'functionalities_assumption_background_scan' );
+ if ( function_exists( 'flush_rewrite_rules' ) ) {
+ \flush_rewrite_rules();
+ }
}
-});
+);
diff --git a/includes/admin/class-admin-ui.php b/includes/admin/class-admin-ui.php
index dc4c91c..84fa2b7 100644
--- a/includes/admin/class-admin-ui.php
+++ b/includes/admin/class-admin-ui.php
@@ -27,9 +27,9 @@ class Admin_UI {
* @param bool $open Whether to show open by default. Default false.
* @return void
*/
- public static function render_docs_section( string $title, string $content, string $type = 'info', bool $open = false ) : void {
+ public static function render_docs_section( string $title, string $content, string $type = 'info', bool $open = false ): void {
$open_attr = $open ? ' open' : '';
- $class = 'functionalities-docs-accordion functionalities-docs-' . esc_attr( $type );
+ $class = 'functionalities-docs-accordion functionalities-docs-' . esc_attr( $type );
echo ' ' . esc_html( $description ) . ' ' . esc_html( $message ) . '
-
-
-
-
- functionalities.dev'
- );
- ?>
- ' . \esc_html__( 'Enter additional CSS classes to remove from content output (one per line). Example: my-plugin-class', 'functionalities' ) . ' ' . \esc_html__( 'Control which post types appear in the block editor link search suggestions.', 'functionalities' ) . ' ' . \esc_html__( 'Enable limitation, then check only the post types you want to appear when searching for links in the editor. Unchecked post types will be hidden from link search results.', 'functionalities' ) . ' ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Insert custom code snippets into your site header and footer without editing theme files.', 'functionalities' ) . ' ';
- echo '<script>, <style>, <link>, <meta>, <noscript>';
- echo ' ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Add Schema.org microdata attributes to improve search engine understanding of your content.', 'functionalities' ) . ' ';
- echo '' . \esc_html__( 'Site:', 'functionalities' ) . ' WebPage, AboutPage, ContactPage, Blog, SearchResultsPage ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Create reusable CSS components that are automatically loaded across your entire site.', 'functionalities' ) . ' ' . \esc_html__( 'Add components by entering a CSS selector (e.g., .my-button) and CSS rules (e.g., background: blue; color: white;). Use the grid below to manage your components.', 'functionalities' ) . ' ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Remove unnecessary WordPress features to improve performance and security.', 'functionalities' ) . ' ' . \esc_html__( 'Some options may break functionality if plugins depend on them. Test after enabling. Disable Heartbeat API with care if you use auto-save or real-time features.', 'functionalities' ) . ' ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Self-host custom fonts with automatic @font-face CSS generation.', 'functionalities' ) . ' ' . \esc_html__( 'Upload font files to your media library or server, then add font entries below with the family name and file URLs. Use the generated font-family name in your CSS.', 'functionalities' ) . ' ';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Protect your login page and customize its appearance.', 'functionalities' ) . ' ' . \esc_html__( 'Only enable when this site sits behind a trusted reverse proxy or CDN (Cloudflare, nginx, etc.). With this OFF, lockouts are keyed by REMOTE_ADDR and cannot be spoofed via headers — the secure default.', 'functionalities' ) . ' ';
- if ( $detected !== 'none' ) {
- echo '✓ ' . \esc_html__( 'Detected:', 'functionalities' ) . ' ' . \esc_html( $plugin_names[ $detected ] ) . '';
- } else {
- echo \esc_html__( 'Supports: Rank Math, Yoast SEO, The SEO Framework, SEOPress, AIOSEO', 'functionalities' );
- }
- echo ' ' . \esc_html__( 'Default license for new posts (can be overridden per-post).', 'functionalities' ) . ' ' . \esc_html__( 'Custom URL for "All Rights Reserved" license (e.g., your terms/disclaimer page).', 'functionalities' ) . ' ' . \esc_html__( 'Select post types where meta tags and license metabox should appear.', 'functionalities' ) . ' ' . \esc_html__( 'Who should be listed as the copyright holder.', 'functionalities' ) . ' ' . \esc_html__( 'Used when "Custom Name" is selected above.', 'functionalities' ) . ' ' . \esc_html__( 'Leave empty to use site language. Use ISO 639 codes (en, en-US, de, etc.).', 'functionalities' ) . ' ' . \esc_html__( 'Detect when internal links are removed from content.', 'functionalities' ) . ' ' . \esc_html__( 'Warn if internal links drop by this percentage or more.', 'functionalities' ) . ' ' . \esc_html__( 'Also warn if this many links are removed (whichever triggers first).', 'functionalities' ) . ' ' . \esc_html__( 'Detect when content is shortened significantly.', 'functionalities' ) . ' ' . \esc_html__( 'Warn if word count drops by this percentage.', 'functionalities' ) . ' ' . \esc_html__( 'Only check word count for posts older than this (to avoid alerts on new posts).', 'functionalities' ) . ' ' . \esc_html__( 'Detect accessibility issues with heading hierarchy.', 'functionalities' ) . ' ' . \esc_html__( 'Configure snapshot and display options.', 'functionalities' ) . ' ' . \esc_html__( 'Number of historical snapshots to retain per post.', 'functionalities' ) . ' ' . \esc_html__( 'Warn when inline CSS exceeds this size.', 'functionalities' ) . ' ' . \esc_html__( 'Basic information about your progressive web app.', 'functionalities' ) . ' ' . \esc_html__( 'Full name displayed on install. Leave blank to use site title.', 'functionalities' ) . ' ' . \esc_html__( 'Short name for the home screen (12 chars max).', 'functionalities' ) . ' ' . \esc_html__( 'Shown in app stores and install dialogs. Leave blank to use site tagline.', 'functionalities' ) . ' ' . \esc_html__( 'Navigation scope. "/" means the entire site.', 'functionalities' ) . ' ' . \esc_html__( 'Comma-separated W3C categories (e.g. news, education, business).', 'functionalities' ) . ' ' . \esc_html__( 'Theme colors and app icons. Icons must be square PNG files.', 'functionalities' ) . ' ' . \esc_html__( 'Customize the in-page install prompt shown to visitors.', 'functionalities' ) . ' ' . \esc_html__( 'Days to wait before showing prompt again after dismissal.', 'functionalities' ) . ' ' . \esc_html__( 'Service worker caching and offline behavior.', 'functionalities' ) . ' ' . \esc_html__( 'Increment to force cache refresh (e.g. v1, v2).', 'functionalities' ) . ' ' . \esc_html__( 'One URL per line. These pages will be cached immediately when the service worker installs.', 'functionalities' ) . ' ' . \esc_html__( 'App shortcut links shown on long-press of the app icon. Up to 4 shortcuts.', 'functionalities' ) . ' ' . \esc_html__( 'App screenshots shown in install dialogs and app stores. Provide wide and narrow sizes.', 'functionalities' ) . ' ' . \esc_html__( 'Advanced PWA features. Only change these if you know what you\'re doing.', 'functionalities' ) . ' ' . \esc_html__( 'Controls behavior when the app is already open and a new navigation is triggered.', 'functionalities' ) . ' ' . \esc_html__( 'Raw JSON to merge into the manifest. Must be a valid JSON object.', 'functionalities' ) . ' ' . \esc_html__( 'One per line. Supports full URLs (https://example.com/page), domains (example.com), or partial matches (e.g., /partner/).', 'functionalities' ) . ' ' . \esc_html__( 'Control how external and internal links are handled across your site.', 'functionalities' ) . ' ' . \esc_html__( 'One domain per line. Matching hosts will NOT be forced to open in a new tab when internal option is enabled.', 'functionalities' ) . '
-
-
-
-
- get( 'Name' ) )
- );
- ?>
-
- get( 'Name' ) )
- );
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ✓ ' . \esc_html__( 'Detected:', 'functionalities' ) . ' ' . \esc_html( $plugin_names[ $detected ] ?? $detected ) . ' ' . \esc_html__( 'Copyright data will be added to your SEO plugin\'s existing schema output.', 'functionalities' ) . ' ✓ ' . \esc_html__( 'Standalone Mode', 'functionalities' ) . ' ' . \esc_html__( 'No SEO plugin detected. Complete Article schema with copyright will be output independently.', 'functionalities' ) . ' ' . \esc_html__( 'Detect structural regressions when posts are updated. This module compares each post against its own historical baseline.', 'functionalities' ) . ' ' . \esc_html( $docs['usage'] ) . ' ' . \esc_html__( 'Detects when technical assumptions stop being true. This module notices changes, not problems.', 'functionalities' ) . ' ' . \esc_html( $docs['usage'] ) . ' ';
- echo '' . \esc_html__( 'Where:', 'functionalities' ) . ' ';
- echo wp_kses( $assumption['location'], array( 'code' => array(), 'strong' => array() ) );
- echo ' ';
- echo '' . \esc_html__( 'Why it matters:', 'functionalities' ) . ' ';
- echo \esc_html( $assumption['reason'] );
- echo ' ' . \esc_html__( 'Settings saved.', 'functionalities' ) . '
-
- ' . \esc_html( WP_CONTENT_DIR . '/functionalities/tasks/' ) . ''
- );
- ?>
- ' . \esc_html__( 'Settings saved.', 'functionalities' ) . ' ' . \esc_html__( 'Settings saved.', 'functionalities' ) . ' ' . \esc_html( $desc ) . ' ' . \sprintf(
- /* translators: %s: WordPress hook name */
- \esc_html__( 'Output in %s. Each snippet can be individually toggled.', 'functionalities' ),
- '
+
+
+ functionalities.dev'
+ );
+ ?>
+ ' . \esc_html__( 'Enter additional CSS classes to remove from content output (one per line). Example: my-plugin-class', 'functionalities' ) . ' ' . \esc_html__( 'Control which post types appear in the block editor link search suggestions.', 'functionalities' ) . ' ' . \esc_html__( 'Enable limitation, then check only the post types you want to appear when searching for links in the editor. Unchecked post types will be hidden from link search results.', 'functionalities' ) . ' ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Insert custom code snippets into your site header and footer without editing theme files.', 'functionalities' ) . ' ';
+ echo '<script>, <style>, <link>, <meta>, <noscript>';
+ echo ' ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Add Schema.org microdata attributes to improve search engine understanding of your content.', 'functionalities' ) . ' ';
+ echo '' . \esc_html__( 'Site:', 'functionalities' ) . ' WebPage, AboutPage, ContactPage, Blog, SearchResultsPage ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Create reusable CSS components that are automatically loaded across your entire site.', 'functionalities' ) . ' ' . \esc_html__( 'Add components by entering a CSS selector (e.g., .my-button) and CSS rules (e.g., background: blue; color: white;). Use the grid below to manage your components.', 'functionalities' ) . ' ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Remove unnecessary WordPress features to improve performance and security.', 'functionalities' ) . ' ' . \esc_html__( 'Some options may break functionality if plugins depend on them. Test after enabling. Disable Heartbeat API with care if you use auto-save or real-time features.', 'functionalities' ) . ' ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Self-host custom fonts with automatic @font-face CSS generation.', 'functionalities' ) . ' ' . \esc_html__( 'Upload font files to your media library or server, then add font entries below with the family name and file URLs. Use the generated font-family name in your CSS.', 'functionalities' ) . ' ';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' ' . \esc_html__( 'Protect your login page and customize its appearance.', 'functionalities' ) . ' ' . \esc_html__( 'Only enable when this site sits behind a trusted reverse proxy or CDN (Cloudflare, nginx, etc.). With this OFF, lockouts are keyed by REMOTE_ADDR and cannot be spoofed via headers — the secure default.', 'functionalities' ) . ' ';
+ if ( $detected !== 'none' ) {
+ echo '✓ ' . \esc_html__( 'Detected:', 'functionalities' ) . ' ' . \esc_html( $plugin_names[ $detected ] ) . '';
+ } else {
+ echo \esc_html__( 'Supports: Rank Math, Yoast SEO, The SEO Framework, SEOPress, AIOSEO', 'functionalities' );
+ }
+ echo ' ' . \esc_html__( 'Default license for new posts (can be overridden per-post).', 'functionalities' ) . ' ' . \esc_html__( 'Custom URL for "All Rights Reserved" license (e.g., your terms/disclaimer page).', 'functionalities' ) . ' ' . \esc_html__( 'Select post types where meta tags and license metabox should appear.', 'functionalities' ) . ' ' . \esc_html__( 'Who should be listed as the copyright holder.', 'functionalities' ) . ' ' . \esc_html__( 'Used when "Custom Name" is selected above.', 'functionalities' ) . ' ' . \esc_html__( 'Leave empty to use site language. Use ISO 639 codes (en, en-US, de, etc.).', 'functionalities' ) . ' ' . \esc_html__( 'Detect when internal links are removed from content.', 'functionalities' ) . ' ' . \esc_html__( 'Warn if internal links drop by this percentage or more.', 'functionalities' ) . ' ' . \esc_html__( 'Also warn if this many links are removed (whichever triggers first).', 'functionalities' ) . ' ' . \esc_html__( 'Detect when content is shortened significantly.', 'functionalities' ) . ' ' . \esc_html__( 'Warn if word count drops by this percentage.', 'functionalities' ) . ' ' . \esc_html__( 'Only check word count for posts older than this (to avoid alerts on new posts).', 'functionalities' ) . ' ' . \esc_html__( 'Detect accessibility issues with heading hierarchy.', 'functionalities' ) . ' ' . \esc_html__( 'Configure snapshot and display options.', 'functionalities' ) . ' ' . \esc_html__( 'Number of historical snapshots to retain per post.', 'functionalities' ) . ' ' . \esc_html__( 'Warn when inline CSS exceeds this size.', 'functionalities' ) . ' ' . \esc_html__( 'Basic information about your progressive web app.', 'functionalities' ) . ' ' . \esc_html__( 'Full name displayed on install. Leave blank to use site title.', 'functionalities' ) . ' ' . \esc_html__( 'Short name for the home screen (12 chars max).', 'functionalities' ) . ' ' . \esc_html__( 'Shown in app stores and install dialogs. Leave blank to use site tagline.', 'functionalities' ) . ' ' . \esc_html__( 'Navigation scope. "/" means the entire site.', 'functionalities' ) . ' ' . \esc_html__( 'Comma-separated W3C categories (e.g. news, education, business).', 'functionalities' ) . ' ' . \esc_html__( 'Theme colors and app icons. Icons must be square PNG files.', 'functionalities' ) . ' ' . \esc_html__( 'Customize the in-page install prompt shown to visitors.', 'functionalities' ) . ' ' . \esc_html__( 'Days to wait before showing prompt again after dismissal.', 'functionalities' ) . ' ' . \esc_html__( 'Service worker caching and offline behavior.', 'functionalities' ) . ' ' . \esc_html__( 'Increment to force cache refresh (e.g. v1, v2).', 'functionalities' ) . ' ' . \esc_html__( 'One URL per line. These pages will be cached immediately when the service worker installs.', 'functionalities' ) . ' ' . \esc_html__( 'App shortcut links shown on long-press of the app icon. Up to 4 shortcuts.', 'functionalities' ) . ' ' . \esc_html__( 'App screenshots shown in install dialogs and app stores. Provide wide and narrow sizes.', 'functionalities' ) . ' ' . \esc_html__( 'Advanced PWA features. Only change these if you know what you\'re doing.', 'functionalities' ) . ' ' . \esc_html__( 'Controls behavior when the app is already open and a new navigation is triggered.', 'functionalities' ) . ' ' . \esc_html__( 'Raw JSON to merge into the manifest. Must be a valid JSON object.', 'functionalities' ) . ' ' . \esc_html__( 'One per line. Supports full URLs (https://example.com/page), domains (example.com), or partial matches (e.g., /partner/).', 'functionalities' ) . ' ' . \esc_html__( 'Control how external and internal links are handled across your site.', 'functionalities' ) . ' ' . \esc_html__( 'One domain per line. Matching hosts will NOT be forced to open in a new tab when internal option is enabled.', 'functionalities' ) . '
+
+
+
+
+ get( 'Name' ) )
+ );
+ ?>
+
+ get( 'Name' ) )
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( $title ) . '
';
@@ -44,7 +44,7 @@ public static function render_docs_section( string $title, string $content, stri
* @param array $items List of feature descriptions.
* @return void
*/
- public static function render_features_docs( array $items ) : void {
+ public static function render_features_docs( array $items ): void {
$content = '';
foreach ( $items as $item ) {
$content .= '
';
foreach ( $hooks as $hook ) {
$content .= '
' . esc_html( $hook['name'] ) . '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_editor_links_enabled — ' . \esc_html__( 'toggle feature', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_editor_links_post_types — ' . \esc_html__( 'modify allowed post types', 'functionalities' );
- echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'Allowed Tags', 'functionalities' ) . '
';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_snippets_output_enabled — ' . \esc_html__( 'disable on specific pages', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_ga4_enabled — ' . \esc_html__( 'control GA4 per user/page', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_header_code — ' . \esc_html__( 'modify header code', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_footer_code — ' . \esc_html__( 'modify footer code', 'functionalities' ) . '
';
- echo \esc_html__( 'Actions:', 'functionalities' ) . ' functionalities_before/after_header/footer_snippets';
- echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'Supported Types', 'functionalities' ) . '
';
- echo '
';
- echo '' . \esc_html__( 'Article:', 'functionalities' ) . ' Article, BlogPosting, NewsArticle';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_schema_enabled — ' . \esc_html__( 'toggle all schema output', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_site_type — ' . \esc_html__( 'modify site itemtype', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_article_type — ' . \esc_html__( 'modify article itemtype', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_content — ' . \esc_html__( 'modify wrapped content', 'functionalities' );
- echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_components_enabled — ' . \esc_html__( 'toggle output', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_components_items — ' . \esc_html__( 'add components dynamically', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_components_css — ' . \esc_html__( 'modify generated CSS', 'functionalities' ) . '
';
- echo \esc_html__( 'Action:', 'functionalities' ) . ' functionalities_components_updated — ' . \esc_html__( 'fires when CSS file regenerates', 'functionalities' );
- echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'Caution', 'functionalities' ) . '
';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_misc_options — ' . \esc_html__( 'modify options before application', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_misc_disable_emojis — ' . \esc_html__( 'control emoji removal', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_misc_disable_embeds — ' . \esc_html__( 'control embed removal', 'functionalities' );
- echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
- echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
- echo 'functionalities_fonts_enabled — ' . \esc_html__( 'toggle output', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_fonts_items — ' . \esc_html__( 'add fonts dynamically', 'functionalities' ) . '
';
- echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_fonts_css — ' . \esc_html__( 'modify generated CSS', 'functionalities' ) . '
';
- echo \esc_html__( 'Action:', 'functionalities' ) . ' functionalities_fonts_before_output';
- echo '' . \esc_html__( 'Security Features', 'functionalities' ) . '
';
- echo '';
- echo '
';
- echo '' . \esc_html__( 'Recent Lockouts', 'functionalities' ) . '
';
- echo '';
- foreach ( $logs as $log ) {
- echo '
';
- echo '';
- foreach ( $docs['features'] as $feature ) {
- $list .= '
';
- Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
- }
-
- if ( ! empty( $docs['hooks'] ) ) {
- $hooks_html = '';
- foreach ( $docs['hooks'] as $hook ) {
- $hooks_html .= '
';
- Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
- }
-
- echo '' . \esc_html( $hook['name'] ) . '
-
-
-
-
-
-
- <?php
-/**
- * Add trusted domains to nofollow exceptions.
- * Links to these domains will NOT have nofollow added.
- */
-add_filter( 'functionalities_exception_domains', function( $domains ) {
- // Add your trusted domains here
- $trusted_domains = array(
- 'trusted-partner.com',
- 'another-trusted-site.org',
- 'your-other-website.net',
- );
-
- return array_merge( $domains, $trusted_domains );
-} );
-
-
-
-
- <?php
-/**
- * Add specific URLs to nofollow exceptions.
- * These specific URLs will NOT have nofollow added.
- */
-add_filter( 'functionalities_exception_urls', function( $urls ) {
- // Add specific URLs or patterns
- $trusted_urls = array(
- 'https://example.com/specific-page',
- 'https://partner.com/affiliate/*',
- 'https://trusted.org/blog/*',
- );
-
- return array_merge( $urls, $trusted_urls );
-} );
-
-
-
-
- <?php
-/**
- * Dynamically add exception domains based on conditions.
- * Example: Trust all domains for administrators.
- */
-add_filter( 'functionalities_exception_domains', function( $domains ) {
- // Skip nofollow for administrators
- if ( current_user_can( 'manage_options' ) ) {
- // Return wildcard to match all domains
- $domains[] = '*';
- }
-
- // Add domains from a custom option
- $custom_domains = get_option( 'my_trusted_domains', array() );
- if ( is_array( $custom_domains ) ) {
- $domains = array_merge( $domains, $custom_domains );
- }
-
- // Add sponsor domains on specific post types
- if ( is_singular( 'sponsored_post' ) ) {
- $domains[] = 'sponsor-website.com';
- }
-
- return $domains;
-} );
-
-
-
-
- <?php
-/**
- * Use a custom JSON file path for exception URLs.
- */
-add_filter( 'functionalities_json_preset_path', function( $default_path ) {
- // Use a JSON file from your theme
- $theme_json = get_stylesheet_directory() . '/config/exceptions.json';
-
- if ( file_exists( $theme_json ) ) {
- return $theme_json;
- }
-
- // Or use an external URL (use with caution!)
- // return 'https://your-cdn.com/exception-urls.json';
-
- return $default_path;
-} );';
- foreach ( $docs['features'] as $feature ) {
- $list .= '
';
- Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
- }
-
- // Classes removed info.
- $classes_html = '';
- $classes_html .= '
';
- Admin_UI::render_docs_section( \__( 'Classes Removed', 'functionalities' ), $classes_html, 'usage' );
-
- if ( ! empty( $docs['hooks'] ) ) {
- $hooks_html = '';
- foreach ( $docs['hooks'] as $hook ) {
- $hooks_html .= '
';
- Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
- }
-
- echo '' . \esc_html( $hook['name'] ) . '
- .new-component
- ';
- foreach ( $docs['features'] as $feature ) {
- $list .= '
';
- Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
- }
-
- // Schema.org Support (dynamic based on detected plugin).
- $plugin_names = array(
- 'rank-math' => 'Rank Math',
- 'yoast' => 'Yoast SEO',
- 'seo-framework' => 'The SEO Framework',
- 'seopress' => 'SEOPress',
- 'aioseo' => 'All in One SEO',
- );
-
- if ( $detected !== 'none' ) {
- $schema_content = '';
- $plugins_html .= '
';
- Admin_UI::render_docs_section( \__( 'Compatible SEO Plugins', 'functionalities' ), $plugins_html, 'usage' );
-
- if ( ! empty( $docs['hooks'] ) ) {
- $hooks_html = '';
- foreach ( $docs['hooks'] as $hook ) {
- $hooks_html .= '
';
- Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
- }
-
- echo '' . \esc_html( $hook['name'] ) . '';
- foreach ( $docs['features'] as $feature ) {
- $list .= '
';
- Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
- }
-
- if ( ! empty( $docs['usage'] ) ) {
- Admin_UI::render_docs_section( \__( 'Philosophy', 'functionalities' ), '';
- foreach ( $docs['hooks'] as $hook ) {
- $hooks_html .= '
';
- Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
- }
-
- echo '' . \esc_html( $hook['name'] ) . '';
- foreach ( $docs['features'] as $feature ) {
- $list .= '
';
- Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
- }
-
- if ( ! empty( $docs['usage'] ) ) {
- Admin_UI::render_docs_section( \__( 'How to Use', 'functionalities' ), '';
- foreach ( $docs['hooks'] as $hook ) {
- $hooks_html .= '
';
- Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
- }
- }
-
- /**
- * Detected assumptions field callback.
- *
- * @return void
- */
- public static function field_detected_assumptions() : void {
- $assumptions = \Functionalities\Features\Assumption_Detection::get_detected_assumptions();
- $ignored = \Functionalities\Features\Assumption_Detection::get_ignored_assumptions();
-
- // Filter out expired ignored items and already-ignored assumptions.
- $active_warnings = array();
- foreach ( $assumptions as $assumption ) {
- $hash = self::generate_warning_hash( $assumption );
- // Skip if ignored and not expired.
- if ( isset( $ignored[ $hash ] ) && $ignored[ $hash ]['expires'] > time() ) {
- continue;
- }
- $assumption['_hash'] = $hash;
- $active_warnings[] = $assumption;
- }
-
- if ( empty( $active_warnings ) ) {
- echo '' . \esc_html( $hook['name'] ) . '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
-
-
-
-
-
-
-
- ' . \esc_html( $hook_name ) . ''
- ) . '
+
+
+
+
+
+
+
+
+
+
+
+ ' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_editor_links_enabled — ' . \esc_html__( 'toggle feature', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_editor_links_post_types — ' . \esc_html__( 'modify allowed post types', 'functionalities' );
+ echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'Allowed Tags', 'functionalities' ) . '
';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_snippets_output_enabled — ' . \esc_html__( 'disable on specific pages', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_ga4_enabled — ' . \esc_html__( 'control GA4 per user/page', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_header_code — ' . \esc_html__( 'modify header code', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_snippets_footer_code — ' . \esc_html__( 'modify footer code', 'functionalities' ) . '
';
+ echo \esc_html__( 'Actions:', 'functionalities' ) . ' functionalities_before/after_header/footer_snippets';
+ echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'Supported Types', 'functionalities' ) . '
';
+ echo '
';
+ echo '' . \esc_html__( 'Article:', 'functionalities' ) . ' Article, BlogPosting, NewsArticle';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_schema_enabled — ' . \esc_html__( 'toggle all schema output', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_site_type — ' . \esc_html__( 'modify site itemtype', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_article_type — ' . \esc_html__( 'modify article itemtype', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_schema_content — ' . \esc_html__( 'modify wrapped content', 'functionalities' );
+ echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_components_enabled — ' . \esc_html__( 'toggle output', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_components_items — ' . \esc_html__( 'add components dynamically', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_components_css — ' . \esc_html__( 'modify generated CSS', 'functionalities' ) . '
';
+ echo \esc_html__( 'Action:', 'functionalities' ) . ' functionalities_components_updated — ' . \esc_html__( 'fires when CSS file regenerates', 'functionalities' );
+ echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'Caution', 'functionalities' ) . '
';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_misc_options — ' . \esc_html__( 'modify options before application', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_misc_disable_emojis — ' . \esc_html__( 'control emoji removal', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_misc_disable_embeds — ' . \esc_html__( 'control embed removal', 'functionalities' );
+ echo '' . \esc_html__( 'What This Module Does', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'How to Use', 'functionalities' ) . '
';
+ echo '' . \esc_html__( 'For Developers', 'functionalities' ) . '
';
+ echo 'functionalities_fonts_enabled — ' . \esc_html__( 'toggle output', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_fonts_items — ' . \esc_html__( 'add fonts dynamically', 'functionalities' ) . '
';
+ echo \esc_html__( 'Filter:', 'functionalities' ) . ' functionalities_fonts_css — ' . \esc_html__( 'modify generated CSS', 'functionalities' ) . '
';
+ echo \esc_html__( 'Action:', 'functionalities' ) . ' functionalities_fonts_before_output';
+ echo '' . \esc_html__( 'Security Features', 'functionalities' ) . '
';
+ echo '';
+ echo '
';
+ echo '' . \esc_html__( 'Recent Lockouts', 'functionalities' ) . '
';
+ echo '';
+ foreach ( $logs as $log ) {
+ echo '
';
+ echo '';
+ foreach ( $docs['features'] as $feature ) {
+ $list .= '
';
+ Admin_UI::render_docs_section( \__( 'What This Module Does', 'functionalities' ), $list, 'info' );
+ }
+
+ if ( ! empty( $docs['hooks'] ) ) {
+ $hooks_html = '';
+ foreach ( $docs['hooks'] as $hook ) {
+ $hooks_html .= '
';
+ Admin_UI::render_docs_section( \__( 'Developer Hooks', 'functionalities' ), $hooks_html, 'developer' );
+ }
+
+ echo '' . \esc_html( $hook['name'] ) . '
+
+
+
+
+
+
+ <?php
+/**
+ * Add trusted domains to nofollow exceptions.
+ * Links to these domains will NOT have nofollow added.
+ */
+add_filter( 'functionalities_exception_domains', function( $domains ) {
+ // Add your trusted domains here
+ $trusted_domains = array(
+ 'trusted-partner.com',
+ 'another-trusted-site.org',
+ 'your-other-website.net',
+ );
+
+ return array_merge( $domains, $trusted_domains );
+} );
+
+
+
+
+ <?php
+/**
+ * Add specific URLs to nofollow exceptions.
+ * These specific URLs will NOT have nofollow added.
+ */
+add_filter( 'functionalities_exception_urls', function( $urls ) {
+ // Add specific URLs or patterns
+ $trusted_urls = array(
+ 'https://example.com/specific-page',
+ 'https://partner.com/affiliate/*',
+ 'https://trusted.org/blog/*',
+ );
+
+ return array_merge( $urls, $trusted_urls );
+} );
+
+
+
+
+ <?php
+/**
+ * Dynamically add exception domains based on conditions.
+ * Example: Trust all domains for administrators.
+ */
+add_filter( 'functionalities_exception_domains', function( $domains ) {
+ // Skip nofollow for administrators
+ if ( current_user_can( 'manage_options' ) ) {
+ // Return wildcard to match all domains
+ $domains[] = '*';
+ }
+
+ // Add domains from a custom option
+ $custom_domains = get_option( 'my_trusted_domains', array() );
+ if ( is_array( $custom_domains ) ) {
+ $domains = array_merge( $domains, $custom_domains );
+ }
+
+ // Add sponsor domains on specific post types
+ if ( is_singular( 'sponsored_post' ) ) {
+ $domains[] = 'sponsor-website.com';
+ }
+
+ return $domains;
+} );
+
+
+
+
+ <?php
+/**
+ * Use a custom JSON file path for exception URLs.
+ */
+add_filter( 'functionalities_json_preset_path', function( $default_path ) {
+ // Use a JSON file from your theme
+ $theme_json = get_stylesheet_directory() . '/config/exceptions.json';
+
+ if ( file_exists( $theme_json ) ) {
+ return $theme_json;
+ }
+
+ // Or use an external URL (use with caution!)
+ // return 'https://your-cdn.com/exception-urls.json';
+
+ return $default_path;
+} );
' . \esc_html( $hook['name'] ) . '
+ .new-component
+ + +
+ ' . esc_html( $label ) . ''; + }, + 'functionalities_misc', + 'functionalities_misc_section' + ); + } + + public static function field_fonts_items(): void { + $o = self::get_fonts_options(); + $items = isset( $o['items'] ) && is_array( $o['items'] ) ? $o['items'] : array(); + $total_items = count( $items ); + + // Enqueue media uploader + \wp_enqueue_media(); + ?> + + ++ +
++ +
++ +
+ + + + + + + ' . \esc_html__( 'Add copyright metadata, Dublin Core (DCMI) tags, and per-post licensing options. Works standalone or integrates with major SEO plugins.', 'functionalities' ) . ''; + + // Get module docs. + $docs = Module_Docs::get( 'meta' ); + + // Render documentation accordions. + echo '✓ ' . \esc_html__( 'Detected:', 'functionalities' ) . ' ' . \esc_html( $plugin_names[ $detected ] ?? $detected ) . '
'; + $schema_content .= '' . \esc_html__( 'Copyright data will be added to your SEO plugin\'s existing schema output.', 'functionalities' ) . '
'; + } else { + $schema_content = '✓ ' . \esc_html__( 'Standalone Mode', 'functionalities' ) . '
'; + $schema_content .= '' . \esc_html__( 'No SEO plugin detected. Complete Article schema with copyright will be output independently.', 'functionalities' ) . '
'; + } + Admin_UI::render_docs_section( \__( 'Schema.org Support', 'functionalities' ), $schema_content, 'developer', true ); + + // Compatible plugins. + $plugins_html = '' . \esc_html( $hook['name'] ) . '' . \esc_html__( 'Detect structural regressions when posts are updated. This module compares each post against its own historical baseline.', 'functionalities' ) . '
'; + + // Get module docs. + $docs = Module_Docs::get( 'content-regression' ); + + // Render documentation accordions. + echo '' . \esc_html( $docs['usage'] ) . '
', 'usage' ); + } + + if ( ! empty( $docs['hooks'] ) ) { + $hooks_html = '' . \esc_html( $hook['name'] ) . '' . \esc_html__( 'Detects when technical assumptions stop being true. This module notices changes, not problems.', 'functionalities' ) . '
'; + + // Get module docs. + $docs = Module_Docs::get( 'assumption-detection' ); + + // Render documentation accordions. + if ( ! empty( $docs['features'] ) ) { + $list = '' . \esc_html( $docs['usage'] ) . '
', 'usage' ); + } + + if ( ! empty( $docs['hooks'] ) ) { + $hooks_html = '' . \esc_html( $hook['name'] ) . ''; + echo '' . \esc_html__( 'Where:', 'functionalities' ) . ' '; + echo wp_kses( + $assumption['location'], + array( + 'code' => array(), + 'strong' => array(), + ) + ); + echo '
'; + } + + // Show reason (why) if available. + if ( ! empty( $assumption['reason'] ) ) { + echo ''; + echo '' . \esc_html__( 'Why it matters:', 'functionalities' ) . ' '; + echo \esc_html( $assumption['reason'] ); + echo '
'; + } + + // Show type badge. + echo '' . \esc_html( str_replace( '_', ' ', ucfirst( $warning_type ) ) ) . ''; + + if ( ! empty( $assumption['detected'] ) ) { + $time_ago = \human_time_diff( $assumption['detected'], \time() ); + /* translators: %s: human-readable time difference */ + echo '' . \sprintf( \esc_html__( 'Detected %s ago', 'functionalities' ), \esc_html( $time_ago ) ) . ''; + } + + echo '' . \esc_html__( 'Settings saved.', 'functionalities' ) . '
+ +
+ + ' . \esc_html( WP_CONTENT_DIR . '/functionalities/tasks/' ) . '' + ); + ?> +
+' . \esc_html__( 'Settings saved.', 'functionalities' ) . '
' . \esc_html__( '404 monitor settings saved.', 'functionalities' ) . '
+ +
| + | + | + | + | + | + |
|---|---|---|---|---|---|
| > | + |
+ + | + | + | + + | +
| ||||
' . \esc_html__( 'Settings saved.', 'functionalities' ) . '
'; + } + ?> + + + + + + true, + 'class' => true, + 'style' => true, + 'fill' => true, + 'fill-opacity' => true, + 'fill-rule' => true, + 'stroke' => true, + 'stroke-width' => true, + 'stroke-linecap' => true, + 'stroke-linejoin' => true, + 'stroke-dasharray' => true, + 'stroke-dashoffset' => true, + 'stroke-opacity' => true, + 'opacity' => true, + 'transform' => true, + 'clip-path' => true, + 'clip-rule' => true, + 'mask' => true, + ); + + return array( + 'svg' => array_merge( + $common_attrs, + array( + 'xmlns' => true, + 'xmlns:xlink' => true, + 'viewbox' => true, + 'width' => true, + 'height' => true, + 'aria-hidden' => true, + 'role' => true, + 'focusable' => true, + 'preserveaspectratio' => true, + 'version' => true, + 'xml:space' => true, + 'enable-background' => true, + ) + ), + 'g' => $common_attrs, + 'path' => array_merge( $common_attrs, array( 'd' => true ) ), + 'circle' => array_merge( + $common_attrs, + array( + 'cx' => true, + 'cy' => true, + 'r' => true, + ) + ), + 'ellipse' => array_merge( + $common_attrs, + array( + 'cx' => true, + 'cy' => true, + 'rx' => true, + 'ry' => true, + ) + ), + 'rect' => array_merge( + $common_attrs, + array( + 'x' => true, + 'y' => true, + 'width' => true, + 'height' => true, + 'rx' => true, + 'ry' => true, + ) + ), + 'line' => array_merge( + $common_attrs, + array( + 'x1' => true, + 'y1' => true, + 'x2' => true, + 'y2' => true, + ) + ), + 'polyline' => array_merge( $common_attrs, array( 'points' => true ) ), + 'polygon' => array_merge( $common_attrs, array( 'points' => true ) ), + 'defs' => array( 'id' => true ), + 'clippath' => array( 'id' => true ), + 'mask' => array( 'id' => true ), + 'use' => array( + 'xlink:href' => true, + 'href' => true, + 'x' => true, + 'y' => true, + 'width' => true, + 'height' => true, + ), + 'symbol' => array( + 'id' => true, + 'viewbox' => true, + 'preserveaspectratio' => true, + ), + 'title' => array(), + 'desc' => array(), + 'lineargradient' => array( + 'id' => true, + 'gradientunits' => true, + 'gradienttransform' => true, + 'spreadmethod' => true, + 'x1' => true, + 'y1' => true, + 'x2' => true, + 'y2' => true, + ), + 'radialgradient' => array( + 'id' => true, + 'gradientunits' => true, + 'gradienttransform' => true, + 'spreadmethod' => true, + 'cx' => true, + 'cy' => true, + 'r' => true, + 'fx' => true, + 'fy' => true, + ), + 'stop' => array( + 'offset' => true, + 'stop-color' => true, + 'stop-opacity' => true, + 'style' => true, + ), + ); + } + + // ------------------------------------------------------------------------- + // PWA Field Renderers + // ------------------------------------------------------------------------- + + /** + * Render a media upload field. + * + * @param string $name Input name attribute. + * @param string $value Current URL value. + * @param string $desc Description text. + * @return void + */ + private static function render_media_field( string $name, string $value, string $desc = '' ): void { + $id = 'func-media-' . \sanitize_key( str_replace( array( '[', ']' ), '-', $name ) ); + echo '' . \esc_html( $desc ) . '
'; + } + echo '' . \sprintf(
+ /* translators: %s: WordPress hook name */
+ \esc_html__( 'Output in %s. Each snippet can be individually toggled.', 'functionalities' ),
+ '' . \esc_html( $hook_name ) . ''
+ ) . '