From c38228486fda131746272ece7072b266d0fe0bcb Mon Sep 17 00:00:00 2001 From: Alex Kazhukhouski Date: Fri, 9 May 2025 14:06:21 +0200 Subject: [PATCH 01/15] HWP Previews plugin RFC --- plugins/README.md | 2 + plugins/hwp-previews/.editorconfig | 18 + plugins/hwp-previews/.gitignore | 1 + plugins/hwp-previews/autoload.php | 50 + plugins/hwp-previews/composer.json | 88 + plugins/hwp-previews/composer.lock | 3527 +++++++++++++ plugins/hwp-previews/hwp-previews.php | 31 + plugins/hwp-previews/package.xml | 4461 +++++++++++++++++ plugins/hwp-previews/phpcs.xml | 307 ++ .../ControlStructures/ElseKeywordSniff.php | 47 + .../phpcs/HWPStandard/ruleset.xml | 4 + plugins/hwp-previews/phpstan.neon.dist | 35 + .../Rules/ClassConstantVarAnnotationRule.php | 41 + plugins/hwp-previews/psalm.xml | 19 + plugins/hwp-previews/src/Plugin.php | 471 ++ .../src/Post/Data/Post_Data_Model.php | 63 + .../Post_Parent_Manager_Interface.php | 20 + .../src/Post/Parent/Post_Parent_Manager.php | 69 + .../Contracts/Post_Slug_Manager_Interface.php | 35 + .../Post_Slug_Repository_Interface.php | 18 + .../src/Post/Slug/Post_Slug_Manager.php | 107 + .../src/Post/Slug/Post_Slug_Repository.php | 41 + .../Post_Statuses_Config_Interface.php | 30 + .../src/Post/Status/Post_Statuses_Config.php | 54 + .../Contracts/Post_Types_Config_Interface.php | 53 + .../src/Post/Type/Post_Types_Config.php | 107 + .../Preview_Link_Placeholder_Resolver.php | 76 + .../src/Preview/Link/Preview_Link_Service.php | 74 + .../Preview_Parameter_Builder_Interface.php | 27 + .../Contracts/Preview_Parameter_Interface.php | 41 + .../Preview/Parameter/Preview_Parameter.php | 79 + .../Parameter/Preview_Parameter_Registry.php | 85 + .../Preview_Template_Resolver_Interface.php | 21 + .../Template/Preview_Template_Resolver.php | 67 + .../Contracts/CPT_Settings_Interface.php | 58 + .../Contracts/General_Settings_Interface.php | 23 + .../Contracts/Menu_Page_Interface.php | 23 + .../Fields/Abstract_Settings_Field.php | 119 + .../src/Settings/Fields/Checkbox_Field.php | 54 + .../src/Settings/Fields/Text_Input_Field.php | 51 + .../src/Settings/Menu/Menu_Page.php | 125 + .../src/Settings/Menu/Submenu_Page.php | 54 + .../src/Settings/Preview_Settings.php | 91 + .../src/Settings/Settings_Cache_Group.php | 113 + .../src/Settings/Settings_Section.php | 79 + .../src/Settings/Tabbed_Settings.php | 135 + .../src/Shared/Abstract_Model.php | 28 + plugins/hwp-previews/src/Shared/Helpers.php | 22 + plugins/hwp-previews/templates/admin.php | 38 + .../templates/admin/settings-page-main.php | 84 + .../templates/admin/settings-page-testing.php | 13 + .../hwp-previews/templates/hwp-preview.php | 29 + 52 files changed, 11308 insertions(+) create mode 100644 plugins/hwp-previews/.editorconfig create mode 100644 plugins/hwp-previews/.gitignore create mode 100644 plugins/hwp-previews/autoload.php create mode 100644 plugins/hwp-previews/composer.json create mode 100644 plugins/hwp-previews/composer.lock create mode 100644 plugins/hwp-previews/hwp-previews.php create mode 100644 plugins/hwp-previews/package.xml create mode 100644 plugins/hwp-previews/phpcs.xml create mode 100644 plugins/hwp-previews/phpcs/HWPStandard/Sniffs/ControlStructures/ElseKeywordSniff.php create mode 100644 plugins/hwp-previews/phpcs/HWPStandard/ruleset.xml create mode 100644 plugins/hwp-previews/phpstan.neon.dist create mode 100644 plugins/hwp-previews/phpstan/Rules/ClassConstantVarAnnotationRule.php create mode 100644 plugins/hwp-previews/psalm.xml create mode 100644 plugins/hwp-previews/src/Plugin.php create mode 100644 plugins/hwp-previews/src/Post/Data/Post_Data_Model.php create mode 100644 plugins/hwp-previews/src/Post/Parent/Contracts/Post_Parent_Manager_Interface.php create mode 100644 plugins/hwp-previews/src/Post/Parent/Post_Parent_Manager.php create mode 100644 plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Manager_Interface.php create mode 100644 plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Repository_Interface.php create mode 100644 plugins/hwp-previews/src/Post/Slug/Post_Slug_Manager.php create mode 100644 plugins/hwp-previews/src/Post/Slug/Post_Slug_Repository.php create mode 100644 plugins/hwp-previews/src/Post/Status/Contracts/Post_Statuses_Config_Interface.php create mode 100644 plugins/hwp-previews/src/Post/Status/Post_Statuses_Config.php create mode 100644 plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php create mode 100644 plugins/hwp-previews/src/Post/Type/Post_Types_Config.php create mode 100644 plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php create mode 100644 plugins/hwp-previews/src/Preview/Link/Preview_Link_Service.php create mode 100644 plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Builder_Interface.php create mode 100644 plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Interface.php create mode 100644 plugins/hwp-previews/src/Preview/Parameter/Preview_Parameter.php create mode 100644 plugins/hwp-previews/src/Preview/Parameter/Preview_Parameter_Registry.php create mode 100644 plugins/hwp-previews/src/Preview/Template/Contracts/Preview_Template_Resolver_Interface.php create mode 100644 plugins/hwp-previews/src/Preview/Template/Preview_Template_Resolver.php create mode 100644 plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php create mode 100644 plugins/hwp-previews/src/Settings/Contracts/General_Settings_Interface.php create mode 100644 plugins/hwp-previews/src/Settings/Contracts/Menu_Page_Interface.php create mode 100644 plugins/hwp-previews/src/Settings/Fields/Abstract_Settings_Field.php create mode 100644 plugins/hwp-previews/src/Settings/Fields/Checkbox_Field.php create mode 100644 plugins/hwp-previews/src/Settings/Fields/Text_Input_Field.php create mode 100644 plugins/hwp-previews/src/Settings/Menu/Menu_Page.php create mode 100644 plugins/hwp-previews/src/Settings/Menu/Submenu_Page.php create mode 100644 plugins/hwp-previews/src/Settings/Preview_Settings.php create mode 100644 plugins/hwp-previews/src/Settings/Settings_Cache_Group.php create mode 100644 plugins/hwp-previews/src/Settings/Settings_Section.php create mode 100644 plugins/hwp-previews/src/Settings/Tabbed_Settings.php create mode 100644 plugins/hwp-previews/src/Shared/Abstract_Model.php create mode 100644 plugins/hwp-previews/src/Shared/Helpers.php create mode 100644 plugins/hwp-previews/templates/admin.php create mode 100644 plugins/hwp-previews/templates/admin/settings-page-main.php create mode 100644 plugins/hwp-previews/templates/admin/settings-page-testing.php create mode 100644 plugins/hwp-previews/templates/hwp-preview.php diff --git a/plugins/README.md b/plugins/README.md index 03982f4..7efcb5a 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -4,6 +4,8 @@ WordPress plugins for the Headless WordPress Toolkit. Each plugin is paired with ## Plugins +- `hwp-previews`: WordPress plugin for previewing posts in a headless environment + - `hwp-cli`: WordPress plugin for CLI operations and status endpoints - NPM Package: `@placeholder/cli` - Features: REST API endpoints, admin interface diff --git a/plugins/hwp-previews/.editorconfig b/plugins/hwp-previews/.editorconfig new file mode 100644 index 0000000..465c828 --- /dev/null +++ b/plugins/hwp-previews/.editorconfig @@ -0,0 +1,18 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab + + +[*.md] +trim_trailing_whitespace = false diff --git a/plugins/hwp-previews/.gitignore b/plugins/hwp-previews/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/plugins/hwp-previews/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/plugins/hwp-previews/autoload.php b/plugins/hwp-previews/autoload.php new file mode 100644 index 0000000..7ec1ef5 --- /dev/null +++ b/plugins/hwp-previews/autoload.php @@ -0,0 +1,50 @@ +' . esc_html( $class ) . '', + '' . esc_html( $file ) . '' + ), + '1.0.0' + ); + + error_log( sprintf( 'HWP Previews: Failed to load class %s, file %s not found', $class, $file ) ); + } +} ); \ No newline at end of file diff --git a/plugins/hwp-previews/composer.json b/plugins/hwp-previews/composer.json new file mode 100644 index 0000000..631a13a --- /dev/null +++ b/plugins/hwp-previews/composer.json @@ -0,0 +1,88 @@ +{ + "name": "hwp/previews", + "version": "1.0.0", + "type": "wordpress-plugin", + "description": "This is a WordPress plugin that provides a preview....", + "keywords": [ + "package", + "dependency", + "autoload" + ], + "homepage": "https://wpengine.com/", + "license": "GPL-2.0-or-later", + "authors": [ + { + "name": "WP Engine Headless OSS Development Team", + "email": "headless-oss@wpengine.com", + "homepage": "https://wpengine.com/" + } + ], + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "automattic/vipwpcs": "^3.0", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "humanmade/psalm-plugin-wordpress": "^3.1", + "johnpbloch/wordpress-core": "^6.8", + "phpcompatibility/phpcompatibility-wp": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "slevomat/coding-standard": "^8.0", + "szepeviktor/phpstan-wordpress": "^2.0" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true + }, + "optimize-autoloader": true, + "platform": { + "php": "7.4" + }, + "preferred-install": "dist", + "sort-packages": true + }, + "extra": { + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "HWP\\Previews\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "HWP\\Previews\\Unit\\": "tests/unit/", + "HWP\\Previews\\Integration\\": "tests/integration/", + "HWP\\Previews\\PHPStan\\": "phpstan/", + "HWPStandard\\": "phpcs/HWPStandard" + } + }, + "scripts": { + "php:lint": "vendor/bin/phpcs", + "php:lint:i": [ + "php ./vendor/bin/phpcs -i" + ], + "php:lint:fix": "vendor/bin/phpcbf", + "php:stan": [ + "phpstan analyze --ansi --memory-limit=2G -v" + ], + "psalm": "psalm", + "psalm:fix": "psalm --alter --issues=all" + }, + "scripts-descriptions": { + }, + "support": { + "docs": "https://github.com/composer/composer/docs", + "email": "headless-oss@wpengine.com", + "forum": "https://github.com/composer/composer/forum", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy" + } +} diff --git a/plugins/hwp-previews/composer.lock b/plugins/hwp-previews/composer.lock new file mode 100644 index 0000000..59169e0 --- /dev/null +++ b/plugins/hwp-previews/composer.lock @@ -0,0 +1,3527 @@ +{ + "_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": "a833cde255a62cc18472ed252c99b7be", + "packages": [], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T18:52:26+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-13T18:00:56+00:00" + }, + { + "name": "automattic/vipwpcs", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/Automattic/VIP-Coding-Standards.git", + "reference": "2b1d206d81b74ed999023cffd924f862ff2753c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/2b1d206d81b74ed999023cffd924f862ff2753c8", + "reference": "2b1d206d81b74ed999023cffd924f862ff2753c8", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.2.1", + "phpcsstandards/phpcsutils": "^1.0.11", + "sirbrillig/phpcs-variable-analysis": "^2.11.18", + "squizlabs/php_codesniffer": "^3.9.2", + "wp-coding-standards/wpcs": "^3.1.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7 || ^8 || ^9" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", + "source": "https://github.com/Automattic/VIP-Coding-Standards", + "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" + }, + "time": "2024-05-10T20:31:09+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "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": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "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", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "time": "2023-01-05T11:28:13+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + }, + "time": "2024-04-30T00:40:11+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "humanmade/psalm-plugin-wordpress", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-wordpress.git", + "reference": "3f4689ad5264eee7b37121053cec810a3754f7e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-wordpress/zipball/3f4689ad5264eee7b37121053cec810a3754f7e4", + "reference": "3f4689ad5264eee7b37121053cec810a3754f7e4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php-stubs/wordpress-globals": "^0.2.0", + "php-stubs/wordpress-stubs": "^6.0", + "php-stubs/wp-cli-stubs": "^2.7", + "vimeo/psalm": "^5 || ^6", + "wp-hooks/wordpress-core": "^1.3.0" + }, + "require-dev": { + "humanmade/coding-standards": "^1.2", + "phpunit/phpunit": "^9.0", + "psalm/plugin-phpunit": "^0.18.4" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "PsalmWordPress\\Plugin" + } + }, + "autoload": { + "psr-4": { + "PsalmWordPress\\": [ + "." + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "kkmuffme", + "role": "Maintainer" + }, + { + "name": "Joe Hoyle", + "role": "Creator" + } + ], + "description": "WordPress stubs and plugin for Psalm static analysis.", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-wordpress/issues", + "source": "https://github.com/psalm/psalm-plugin-wordpress" + }, + "time": "2024-04-01T10:36:11+00:00" + }, + { + "name": "johnpbloch/wordpress-core", + "version": "6.8.0", + "source": { + "type": "git", + "url": "https://github.com/johnpbloch/wordpress-core.git", + "reference": "74197a5012b0a72834ffc58bb32ef0045f15a26c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/74197a5012b0a72834ffc58bb32ef0045f15a26c", + "reference": "74197a5012b0a72834ffc58bb32ef0045f15a26c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.2.24" + }, + "provide": { + "wordpress/core-implementation": "6.8.0" + }, + "type": "wordpress-core", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "WordPress Community", + "homepage": "https://wordpress.org/about/" + } + ], + "description": "WordPress is open source software you can use to create a beautiful website, blog, or app.", + "homepage": "https://wordpress.org/", + "keywords": [ + "blog", + "cms", + "wordpress" + ], + "support": { + "forum": "https://wordpress.org/support/", + "irc": "irc://irc.freenode.net/wordpress", + "issues": "https://core.trac.wordpress.org/", + "source": "https://core.trac.wordpress.org/browser", + "wiki": "https://codex.wordpress.org/" + }, + "time": "2025-04-15T15:47:20+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.5.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" + }, + "time": "2024-09-08T10:13:13+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-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/v4.19.4" + }, + "time": "2024-09-29T15:01:53+00:00" + }, + { + "name": "php-stubs/wordpress-globals", + "version": "v0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-globals.git", + "reference": "748a1fb2ae8fda94844bd0545935095dbf404b32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-globals/zipball/748a1fb2ae8fda94844bd0545935095dbf404b32", + "reference": "748a1fb2ae8fda94844bd0545935095dbf404b32", + "shasum": "" + }, + "require-dev": { + "php": "~7.1" + }, + "suggest": { + "php-stubs/wordpress-stubs": "Up-to-date WordPress function and class declaration stubs", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Global variables and global constants from WordPress core.", + "homepage": "https://github.com/php-stubs/wordpress-globals", + "keywords": [ + "PHPStan", + "constants", + "globals", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-globals/issues", + "source": "https://github.com/php-stubs/wordpress-globals/tree/master" + }, + "time": "2020-01-13T06:12:59+00:00" + }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "1824db4d1d00d32c0119175d2369d9425dbc4953" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/1824db4d1d00d32c0119175d2369d9425dbc4953", + "reference": "1824db4d1d00d32c0119175d2369d9425dbc4953", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^4.13", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.3", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.5", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.0" + }, + "time": "2025-04-17T15:13:53+00:00" + }, + { + "name": "php-stubs/wp-cli-stubs", + "version": "v2.11.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wp-cli-stubs.git", + "reference": "f27ff9e8e29d7962cb070e58de70dfaf63183007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/f27ff9e8e29d7962cb070e58de70dfaf63183007", + "reference": "f27ff9e8e29d7962cb070e58de70dfaf63183007", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0" + }, + "require-dev": { + "php": "~7.3 || ~8.0", + "php-stubs/generator": "^0.8.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wp-cli-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress", + "wp-cli" + ], + "support": { + "issues": "https://github.com/php-stubs/wp-cli-stubs/issues", + "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.11.0" + }, + "time": "2024-11-25T10:09:13+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.3", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "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" + } + ], + "time": "2024-04-24T21:30:46+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.6", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/80ccb1a7640995edf1b87a4409fa584cd5869469", + "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0" + }, + "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" + } + ], + "time": "2025-01-16T22:34:19+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0.9", + "squizlabs/php_codesniffer": "^3.8.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "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" + } + ], + "time": "2023-12-08T16:49:07+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.12", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c", + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c", + "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.10.0 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.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", + "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" + } + ], + "time": "2024-05-20T13:34:27+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + }, + "time": "2025-04-13T19:20:35+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + }, + "time": "2025-02-19T13:28:12+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.12", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/96dde49e967c0c22812bcfa7bda4ff82c09f3b0c", + "reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-04-16T13:19:18+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4" + }, + "time": "2025-03-18T11:42:40+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+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": "sirbrillig/phpcs-variable-analysis", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/4debf5383d9ade705e0a25121f16c3fecaf433a7", + "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "^3.5.6" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", + "phpcsstandards/phpcsdevcs": "^1.1", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0" + }, + "type": "phpcodesniffer-standard", + "autoload": { + "psr-4": { + "VariableAnalysis\\": "VariableAnalysis/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" + }, + { + "name": "Payton Swick", + "email": "payton@foolord.com" + } + ], + "description": "A PHPCS sniff to detect problems with variables.", + "keywords": [ + "phpcs", + "static analysis" + ], + "support": { + "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", + "source": "https://github.com/sirbrillig/phpcs-variable-analysis", + "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" + }, + "time": "2025-03-17T16:17:38+00:00" + }, + { + "name": "slevomat/coding-standard", + "version": "8.17.0", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "ace04a4e2e20c9bc26ad14d6c4c737cde6056ec0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/ace04a4e2e20c9bc26ad14d6c4c737cde6056ec0", + "reference": "ace04a4e2e20c9bc26ad14d6c4c737cde6056ec0", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", + "php": "^7.4 || ^8.0", + "phpstan/phpdoc-parser": "^2.1.0", + "squizlabs/php_codesniffer": "^3.12.1" + }, + "require-dev": { + "phing/phing": "3.0.1", + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/phpstan": "2.1.11", + "phpstan/phpstan-deprecation-rules": "2.0.1", + "phpstan/phpstan-phpunit": "2.0.6", + "phpstan/phpstan-strict-rules": "2.0.4", + "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.17|12.1.2" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/8.17.0" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2025-04-10T06:06:16+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "2.17.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^7.4|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "phpunit/phpunit": "^9.0", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-26T08:22:07+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.12.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", + "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", + "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", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "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-04-13T04:10:18+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T11:30:55+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:11:13+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-22T13:05:35+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f37b419f7aea2e9abf10abd261832cace12e3300" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f37b419f7aea2e9abf10abd261832cace12e3300", + "reference": "f37b419f7aea2e9abf10abd261832cace12e3300", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:11:13+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "136ca7d72f72b599f2631aca474a4f8e26719799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799", + "reference": "136ca7d72f72b599f2631aca474a4f8e26719799", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-10T20:33:58+00:00" + }, + { + "name": "szepeviktor/phpstan-wordpress", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/szepeviktor/phpstan-wordpress.git", + "reference": "f7beb13cd22998e3d913fdb897a1e2553ccd637e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/f7beb13cd22998e3d913fdb897a1e2553ccd637e", + "reference": "f7beb13cd22998e3d913fdb897a1e2553ccd637e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "php-stubs/wordpress-stubs": "^6.6.2", + "phpstan/phpstan": "^2.0" + }, + "require-dev": { + "composer/composer": "^2.1.14", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.0", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.0", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "swissspidy/phpstan-no-private": "Detect usage of internal core functions, classes and methods" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "SzepeViktor\\PHPStan\\WordPress\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress extensions for PHPStan", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", + "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.1" + }, + "time": "2024-12-01T02:13:05+00:00" + }, + { + "name": "vimeo/psalm", + "version": "5.26.1", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.17", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2024-09-08T18:53:08+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/9333efcbff231f10dfd9c56bb7b65818b4733ca7", + "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.2.1", + "phpcsstandards/phpcsutils": "^1.0.10", + "squizlabs/php_codesniffer": "^3.9.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^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": "2024-03-25T16:39:00+00:00" + }, + { + "name": "wp-hooks/wordpress-core", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/wp-hooks/wordpress-core-hooks.git", + "reference": "127af21a918a52bcead7ce9b743b17b5d64eb148" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-hooks/wordpress-core-hooks/zipball/127af21a918a52bcead7ce9b743b17b5d64eb148", + "reference": "127af21a918a52bcead7ce9b743b17b5d64eb148", + "shasum": "" + }, + "replace": { + "johnbillion/wp-hooks": "*" + }, + "require-dev": { + "erusev/parsedown": "1.8.0-beta-7", + "oomphinc/composer-installers-extender": "^2", + "roots/wordpress-core-installer": "^1.0.0", + "roots/wordpress-full": "6.8", + "wp-hooks/generator": "1.0.0" + }, + "type": "library", + "extra": { + "wp-hooks": { + "ignore-files": [ + "wp-admin/includes/deprecated.php", + "wp-admin/includes/ms-deprecated.php", + "wp-content/", + "wp-includes/deprecated.php", + "wp-includes/ID3/", + "wp-includes/ms-deprecated.php", + "wp-includes/pomo/", + "wp-includes/random_compat/", + "wp-includes/Requests/", + "wp-includes/SimplePie/", + "wp-includes/sodium_compat/", + "wp-includes/Text/" + ], + "ignore-hooks": [ + "load-categories.php", + "load-edit-link-categories.php", + "load-edit-tags.php", + "load-page-new.php", + "load-page.php", + "option_enable_xmlrpc", + "edit_post_{$field}", + "pre_post_{$field}", + "post_{$field}", + "pre_option_enable_xmlrpc", + "$page_hook", + "$hook", + "$hook_name" + ] + }, + "wordpress-install-dir": "vendor/wordpress/wordpress" + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "John Blackbourn", + "homepage": "https://johnblackbourn.com/" + } + ], + "description": "All the actions and filters from WordPress core in machine-readable JSON format.", + "support": { + "issues": "https://github.com/wp-hooks/wordpress-core-hooks/issues", + "source": "https://github.com/wp-hooks/wordpress-core-hooks/tree/1.10.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/johnbillion", + "type": "github" + } + ], + "time": "2025-04-16T22:20:41+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^7.4 || ^8.0" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "7.4" + }, + "plugin-api-version": "2.6.0" +} diff --git a/plugins/hwp-previews/hwp-previews.php b/plugins/hwp-previews/hwp-previews.php new file mode 100644 index 0000000..cdbcf30 --- /dev/null +++ b/plugins/hwp-previews/hwp-previews.php @@ -0,0 +1,31 @@ + HWP\Previews\Plugin::get_instance()->init( + '0.0.1', + plugin_dir_path( __FILE__ ), +), 5, 0 ); diff --git a/plugins/hwp-previews/package.xml b/plugins/hwp-previews/package.xml new file mode 100644 index 0000000..ae19026 --- /dev/null +++ b/plugins/hwp-previews/package.xml @@ -0,0 +1,4461 @@ + + + xdebug + pecl.php.net + Xdebug is a debugging and productivity extension for PHP + Xdebug and provides a range of features to improve the PHP development +experience. + +Step Debugging + A way to step through your code in your IDE or editor while the script is + executing. + +Improvements to PHP's error reporting + An improved var_dump() function, stack traces for Notices, Warnings, Errors + and Exceptions to highlight the code path to the error + +Tracing + Writes every function call, with arguments and invocation location to disk. + Optionally also includes every variable assignment and return value for + each function. + +Profiling + Allows you, with the help of visualisation tools, to analyse the + performance of your PHP application and find bottlenecks. + +Code Coverage Analysis + To show which parts of your code base are executed when running unit tests + with PHP Unit. + + Derick Rethans + derick + derick@xdebug.org + yes + + 2025-03-09 + + + 3.4.2 + 3.4.2 + + + stable + stable + + Xdebug-1.03 + +Sun, Mar 09, 2025 - Xdebug 3.4.2 + += Fixed bugs: + + - Fixed issue #2313: var_dump does not output some Russian characters + - Fixed issue #2314: Class properties with hooks are always shown as null + - Fixed issue #2315: xdebug_dump_superglobals() leaks memory + - Fixed issue #2317: Code coverage leaks memory + - Fixed issue #2321: Segfault when null is assigned to a superglobal + - Fixed issue #2323: xdebug_notify() does not respect xdebug.var_display_max_* Settings + - Fixed issue #2327: Segmentation Fault 139 if exception thrown in callback since PHP 8.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 8.0.0 + 8.4.99 + + + 1.9.1 + + + + xdebug + + + + 2025-01-06 + + + 3.4.1 + 3.4.1 + + + stable + stable + + Xdebug-1.03 + +Mon, Jan 06, 2025 - Xdebug 3.4.1 + += Fixed bugs: + + - Fixed issue #2306: Segmentation fault on each HTTP request when not listening to debugging connections + - Fixed issue #2307: Segmentation fault due to a superglobal being a reference while checking for triggers + - Fixed issue #2309: Installation on Windows with PHP PIE failing + - Fixed issue #2310: xdebug 3.4.0 crashes php8.1-fpm after script execution + + + + 2024-11-28 + + + 3.4.0 + 3.4.0 + + + stable + stable + + Xdebug-1.03 + +Thu, Nov 28, 2024 - Xdebug 3.4.0 + ++ New features: + + - Fixed issue #2239: Add 'XDEBUG_IGNORE' GET/POST/COOKIE/ENV to make the step debugger ignore that specific request + - Fixed issue #2281: PHP 8.4 support + ++ Improvements + + - Fixed issue #2261: Send control socket location in init packet + += Fixed bugs: + + - Fixed issue #2262: PHP 8.4: Closure names need different wrapping algorithm + - Fixed issue #2283: SoapClient usage causes segfault with codecoverage + - Fixed issue #2294: Nette Tester always crashes in all test jobs when running with XDebug 3.4.0beta1 active + - Fixed issue #2304: Seg fault on throw exception + - Fixed issue #2305: Segfault when checking whether to ignore creating a debug connection during shutdown functions + + + + 2024-10-04 + + + 3.4.0beta1 + 3.4.0beta1 + + + beta + beta + + Xdebug-1.03 + +Fri, Oct 04, 2024 - Xdebug 3.4.0beta1 + += Fixed bugs: + + - Fixed issue #2261: Send control socket location in init packet + - Fixed issue #2281: PHP 8.4 support + + + + 2024-05-31 + + + 3.4.0alpha1 + 3.4.0alpha1 + + + beta + beta + + Xdebug-1.03 + +Fri, May 31, 2024 - Xdebug 3.4.0alpha1 + += Fixed bugs: + + - Fixed issue #2239: Add 'XDEBUG_IGNORE' GET/POST/COOKIE/ENV to make the step debugger ignore that specific request + - Fixed issue #2262: PHP 8.4: Closure names need different wrapping algorithm + + + + 2024-04-15 + + + 3.3.2 + 3.3.2 + + + stable + stable + + Xdebug-1.03 + +Mon, Apr 15, 2024 - Xdebug 3.3.2 + += Fixed bugs: + + - Fixed issue #2216: With PHP8.3 and Apache 2.4.58 error_reporting() causing Apache process to hang + - Fixed issue #2230: Crash when xdebug and blackfire extensions are active + - Fixed issue #2233: High and continuous Apache server CPU use + + + + 2023-12-14 + + + 3.3.1 + 3.3.1 + + + stable + stable + + Xdebug-1.03 + +Thu, Dec 14, 2023 - Xdebug 3.3.1 + += Fixed bugs: + + - Fixed issue #2220: Test failure + - Fixed issue #2221: Crash when other extensions run PHP code without the stack being initialised yet + - Fixed issue #2223: Xdebug's constants are not available with `xdebug.mode=off` + - Fixed issue #2226: xdebug_get_function_stack(['from_exception']) does not always find stored trace + - Fixed issue #2227: Crash with return value and observers + - Fixed issue #2228: Return value can not be fetched with property_get if top frame is an internal function + + + + 2023-11-30 + + + 3.3.0 + 3.3.0 + + + stable + stable + + Xdebug-1.03 + +Thu, Nov 30, 2023 - Xdebug 3.3.0 + ++ New features: + + - Fixed issue #2171: Support for PHP 8.3 + - Fixed issue #1732: Add support for flame graph outputs + - Fixed issue #2219: Add control socket on Linux to obtain information and initiate debugger or breakpoint + - Fixed issue #1562: Add 'local_vars' option to 'xdebug_get_function_stack' to include variables for each st + - Fixed issue #2194: Add 'params_as_values' option to 'xdebug_get_function_stack' to return data as values + - Fixed issue #2195: Add 'from_exception' option to 'xdebug_get_function_stack' to return the stack trace where an exception was thrown + ++ Improvements: + + - Fixed issue #2077: Bring back xdebug.collect_params + - Fixed issue #2170: Show contents of Spl's ArrayIterator + - Fixed issue #2172: Show contents of SplDoublyLinkedList and SplPriorityQueue + - Fixed issue #2183: Bubble up exception message when using code evalution through protocol + - Fixed issue #2188: Step over with fibers does still step into fiber routines + - Fixed issue #2197: Add time index and memory to output of xdebug_get_function_stack + - Fixed issue #2203: Increase default max nesting time out from 256 to 512 + - Fixed issue #2206: Optimise debugger breakpoints checking + - Fixed issue #2207: Add filenames for include and friends to flamegraph output + - Fixed issue #2217: xdebug://gateway pseudo host does not support IPv6 + += Fixed bugs: + + - Fixed issue #450: "Incomplete" backtraces when an exception gets rethrown + - Fixed issue #476: Exception chaining does not work properly + - Fixed issue #1155: Local variables are not shown when execution break in error_handler + - Fixed issue #2000: Debugger evaluate expression: "can't evaluate expression" + - Fixed issue #2027: Branch/path code coverage for traits drops trait name since 3.1.0 + - Fixed issue #2132: Errors when mountinfo does not have enough information for finding systemd private tmp directory + - Fixed issue #2200: PECL package file has wrong max PHP version number, and peclweb refuses the package + - Fixed issue #2208: Superfluous `...` (three omission dots) in var_dump() + - Fixed issue #2210: Flamegraphs crash when using `start_with_request` + - Fixed issue #2211: File wrappers get wrong filename location in stack. + - Fixed issue #2214: Array keys aren't escaped in traces + + + + 2023-10-19 + + + 3.3.0alpha3 + 3.3.0alpha3 + + + beta + beta + + Xdebug-1.03 + +Thu, Oct 19, 2023 - Xdebug 3.3.0alpha3 + += Fixed bugs: + + - Fixed issue #1732: Add support for flame graph outputs + - Fixed issue #2000: Debugger evaluate expression: "can't evaluate expression" + - Fixed issue #2077: Bring back xdebug.collect_params + - Fixed issue #2203: Increase default max nesting time out from 256 to 512 + - Fixed issue #2206: Optimise debugger breakpoints checking + + + + 2023-09-06 + + + 3.3.0alpha2 + 3.3.0alpha2 + + + beta + beta + + Xdebug-1.03 + +Wed, Sep 06, 2023 - Xdebug 3.3.0alpha2 + += Fixed bugs: + + - Fixed issue #2200: PECL package file has wrong max PHP version number, and peclweb refuses the package + + + + 2023-09-06 + + + 3.3.0alpha1 + 3.3.0alpha1 + + + beta + beta + + Xdebug-1.03 + +Wed, Sep 06, 2023 - Xdebug 3.3.0alpha1 + ++ New features: + + - Fixed issue #2171: Support for PHP 8.3 + ++ Improvements: + + - Fixed issue #1562: Add 'local_vars' option to 'xdebug_get_function_stack' to include variables for each st + - Fixed issue #2170: Show contents of Spl's ArrayIterator while debugging + - Fixed issue #2172: Show contents of SplDoublyLinkedList and SplPriorityQueue while debugging + - Fixed issue #2183: Bubble up exception message when using code evalution through protocol + - Fixed issue #2188: Step over with fibers does still step into fiber routines + - Fixed issue #2194: Add 'params_as_values' option to 'xdebug_get_function_stack' to return data as values + - Fixed issue #2195: Add 'from_exception' option to 'xdebug_get_function_stack' to return the stack trace where an exception was thrown + - Fixed issue #2197: Add time index and memory to output of xdebug_get_function_stack + += Fixed bugs: + + - Fixed issue #450: "Incomplete" backtraces when an exception gets rethrown + - Fixed issue #476: Exception chaining does not work properly + - Fixed issue #2132: Errors when mountinfo does not have enough information for finding systemd private tmp directory + + + + 2023-07-14 + + + 3.2.2 + 3.2.2 + + + stable + stable + + Xdebug-1.03 + +Fri, Jul 14, 2023 - Xdebug 3.2.2 + += Fixed bugs: + + - Fixed issue #2175: Crash with EXC_BAD_ACCESS in xdebug_str_create + - Fixed issue #2180: Crash on extended SplFixedArray + - Fixed issue #2182: Segfault with ArrayObject on stack + - Fixed issue #2186: Segfault with trampoline functions and debugger activation + + + + 2023-03-21 + + + 3.2.1 + 3.2.1 + + + stable + stable + + Xdebug-1.03 + +Tue, Mar 21, 2023 - Xdebug 3.2.1 + += Fixed bugs: + + - Fixed issue #2144: Xdebug 3.2.0 ignores xdebug.mode and enables all features + - Fixed issue #2145: Xdebug 3.2.0 crash PHP on Windows if xdebug.mode = off + - Fixed issue #2146: apache2 segfaulting with version 3.2.0 on PHP 8.0 + - Fixed issue #2148: Icon for link to docs in xdebug_info() HTML output does not always render correctly + + + + 2022-12-08 + + + 3.2.0 + 3.2.0 + + + stable + stable + + BSD style + +Thu, Dec 08, 2022 - Xdebug 3.2.0 + ++ New features: + + - Fixed issue #1819: Allow a list of headers in 'xdebug.client_discovery_header' + - Fixed issue #2079: Add pseudo hosts xdebug://gateway and xdebug://nameserver + - Fixed issue #2086: Include return value in return breakpoint interruption response + - Fixed issue #2087: Introduce step for the return state and virtual property for return value + ++ Improvements: + + - Fixed issue #2062: Xdebug now records whether systemd's PrivateTmp is used in its diagnostics information + - Fixed issue #2104: Add support for PHP 8.2 "SensitiveParameter" attribute + - Fixed issue #2117: Removed emulated properties for closures, as PHP 8.2 adds debug information for them + - Fixed issue #2122: Local variables are now available when using start_upon_error + - Fixed issue #2123: Add warning in log and diagnositics information when a breakpoint is set on a non-existing file + - Fixed issue #2138: Step debugger now disconnects and continues running the script, when the debugging client closes the connection + - Fixed issue #2136: Duplicate line/conditional breakpoints are now rejected + +- Deprecations: + + - Fixed issue #2014: Drop support for PHP 7.2 + - Fixed issue #2102: Drop support for PHP 7.3 + - Fixed issue #2103: Drop support for PHP 7.4 + += Fixed bugs: + + - Fixed issue #2002: xdebug_trace_handler_t handler members are not always checked for NULL when executing + - Fixed issue #2045: Inapproriate frowny face + - Fixed issue #2089: Alpine Linux does not support res_ninit + - Fixed issue #2093: Fatal error: linux/rtnetlink.h: No such file or directory linux/rtnetlink.h + - Fixed issue #2098: With breakpoint_include_return_value enabled step_out break at every function + - Fixed issue #2105: 3.2.0alpha1 package misses the php-header.h file + - Fixed issue #2108: Segfault on PHP8.1 with PHPUnit 10 when path coverage is enabled + - Fixed issue #2113: Crash at step_into after thrown exception with return value debugging en + - Fixed issue #2121: Xdebug does not use local independent float-to-string functions + - Fixed issue #2124: Xdebug incorrectly reports that there are no children for static closure properties, even though there are + - Fixed issue #2125: Crash with PHP 8.2 on 32-bit due to change in "not set" value with CATCH opcode + - Fixed issue #2126: Problems with retrieving global variables + - Fixed issue #2127: Tracing does not handle NUL char in anonymous closure scope + - Fixed issue #2133: Warning with regards to extra NUL character in xdebug_setcookie call + - Fixed issue #2134: Xdebug stops at the line where the exception is created, not where it is thrown + - Fixed issue #2135: Xdebug stops twice at the same line after a call breakpoint or xdebug_break() + + + + 2022-11-10 + + + 3.2.0RC2 + 3.2.0RC2 + + + beta + beta + + BSD style + +Thu, Nov 10, 2022 - Xdebug 3.2.0RC2 + += Fixed bugs: + + - Fixed issue #2100: "Fatal error: debuginfo() must return an array" when Exception is thrown from debugInfo in PHP 8.x + - Fixed issue #2101: When a temporary breakpoint is hit, breakpoint_list should show it as disabled + - Fixed issue #2126: Problems with retrieving global variables + - Fixed issue #2127: Tracing does not handle NUL char in anonymous closure scope + - Fixed issue #2129: Cannot read snapshot Gzip-compressed data is corrupt + - Fixed issue #2133: Warning with regards to extra NUL character in xdebug_setcookie call + - Fixed issue #2134: Xdebug stops at the line where the exception is created, not where it is thrown + - Fixed issue #2135: Xdebug stops twice at the same line after a call breakpoint or xdebug_break() + - Fixed issue #2136: Duplicate line/conditional breakpoints are not rejected + + + + 2022-10-10 + + + 3.2.0RC1 + 3.2.0RC1 + + + beta + beta + + BSD style + +Mon, Oct 10, 2022 - Xdebug 3.2.0RC1 + += Fixed bugs: + + - Fixed issue #2113: Crash at step_into after thrown exception with return value debugging en + - Fixed issue #2117: Removed emulated properties for closures, as PHP 8.2 adds debug information for them + - Fixed issue #2121: Xdebug does not use local independent float-to-string functions + - Fixed issue #2122: Local variables are not available when using start_upon_error + - Fixed issue #2123: Add warning in log and diagnositics information when a breakpoint is set on a non-existing file + - Fixed issue #2124: Xdebug incorrectly reports that there are no children for static closure properties, even thought there are + - Fixed issue #2125: Crash with PHP 8.2 on 32-bit due to change in "not set" value with CATCH opcode + + + + 2022-08-24 + + + 3.2.0alpha3 + 3.2.0alpha3 + + + beta + beta + + BSD style + +Wed, Aug 24, 2022 - Xdebug 3.2.0alpha3 + ++ Improvements: + + - Fixed issue #2112: Force 'return_value' breakpoint information and step to 'on' temporarily + + + + 2022-07-25 + + + 3.2.0alpha2 + 3.2.0alpha2 + + + beta + beta + + BSD style + +Mon, Jul 25, 2022 - Xdebug 3.2.0alpha2 + += Fixed bugs: + + - Fixed issue #2105: 3.2.0alpha1 package misses the php-header.h file + + + + 2022-07-20 + + + 3.2.0alpha1 + 3.2.0alpha1 + + + beta + beta + + BSD style + +Wed, Jul 20, 2022 - Xdebug 3.2.0alpha1 + ++ New features: + + - Fixed issue #1819: Allow a list of headers in 'xdebug.client_discovery_header' + - Fixed issue #2079: Add pseudo hosts xdebug://gateway and xdebug://nameserver + - Fixed issue #2087: Introduce step for the return state and virtual property for return value + - Fixed issue #2104: Add support for PHP 8.2 "SensitiveParameter" attribute + ++ Improvements: + + - Fixed issue #2086: Include return value in return breakpoint interruption response + +- Removed features: + + - Fixed issue #2014: Drop support for PHP 7.2 + - Fixed issue #2102: Drop support for PHP 7.3 + - Fixed issue #2103: Drop support for PHP 7.4 + += Fixed bugs: + + - Fixed issue #2002: xdebug_trace_handler_t handler members are not always checked for NULL when executing + - Fixed issue #2045: Inapproriate frowny face + - Fixed issue #2062: Profiler can't able to write cachegrind file at /tmp + - Fixed issue #2089: Alpine Linux does not support res_ninit + - Fixed issue #2093: Fatal error: linux/rtnetlink.h: No such file or directory linux/rtnetlink.h + - Fixed issue #2098: With breakpoint_include_return_value enabled step_out break at every function + + + + 2022-11-08 + + + 3.1.6 + 3.1.6 + + + stable + stable + + BSD style + +Tue, Nov 08, 2022 - Xdebug 3.1.6 + += Fixed bugs: + + - Fixed issue #2100: "Fatal error: debuginfo() must return an array" when Exception is thrown from debugInfo in PHP 8.x + - Fixed issue #2101: When a temporary breakpoint is hit, breakpoint_list should show it as disabled + - Fixed issue #2129: Cannot read snapshot Gzip-compressed data is corrupt + + + + 2022-06-06 + + + 3.1.5 + 3.1.5 + + + stable + stable + + BSD style + +Mon, Jun 06, 2022 - Xdebug 3.1.5 + += Fixed bugs: + + - Fixed issue #2056: Install documentation gives wrong arch for installation on M1 Macs + - Fixed issue #2082: phpize --clean removes required clocks.m4 file + - Fixed issue #2083: Constant defined with an enum case produce double "facet" attribute in context_get response + - Fixed issue #2085: Crash when used with source guardian encoded files + - Fixed issue #2090: Segfault in __callStatic() after FFI initialization + + + + 2022-04-04 + + + 3.1.4 + 3.1.4 + + + stable + stable + + BSD style + +Mon, Apr 04, 2022 - Xdebug 3.1.4 + += Fixed bugs: + + - Fixed issue #2006: Removing second call breakpoint with same function name + - Fixed issue #2060: XDebug breaks the Symfony "PhpFilesAdapter" cache adapter + - Fixed issue #2061: Possible use after free with GC Stats + - Fixed issue #2063: Can't inspect ArrayObject storage elements + - Fixed issue #2064: Segmentation fault in symfony cache + - Fixed issue #2068: Debug session can be started with "XDEBUG_SESSION_START=anything" when xdebug.trigger_value is set + - Fixed issue #2069: Warn when profiler_append is used together with zlib compression + - Fixed issue #2075: Code coverage misses static array assignment lines + + + + 2022-02-01 + + + 3.1.3 + 3.1.3 + + + stable + stable + + BSD style + +Tue, Feb 01, 2022 - Xdebug 3.1.3 + += Fixed bugs: + + - Fixed issue #2049: evaling broken code (still) causes unhandled exception in PHP 7.4 + - Fixed issue #2052: Memory leak when a trace file can't be opened because xdebug.trace_output_name is invalid + - Fixed issue #2054: Slowdown when calling a function with long string parameters + - Fixed issue #2055: Debugger creates XML with double facet attribute + + + + 2021-12-01 + + + 3.1.2 + 3.1.2 + + + stable + stable + + BSD style + +Wed, Dec 01, 2021 - Xdebug 3.1.2 + += Fixed bugs: + + - Fixed issue #2036: Segfault on fiber switch in finally block in garbage collected fiber + - Fixed issue #2037: Crash when profile file can not be created + - Fixed issue #2041: __debugInfo is not used for var_dump output + - Fixed issue #2046: Segault on xdebug_get_function_stack inside a Fiber + + + + 2021-10-15 + + + 3.1.1 + 3.1.1 + + + stable + stable + + BSD style + +Fri, Oct 15, 2021 - Xdebug 3.1.1 + += Fixed bugs: + + - Fixed issue #2016: apache gives no output with xdebug 3.1.0b2 installed + - Fixed issue #2024: Apache restarts in a loop under PHP 8.1.0 RC3 + - Fixed issue #2029: incorrect and inaccurate date and time displayed in xdebug.log and trace files + - Fixed issue #2030: PhpStorm step-debug not working on PHP 8.0.11 + - Fixed issue #2032: Use runtime PHP version in DBGp and info pages instead of compiled-against version + - Fixed issue #2034: Xdebug throws a Segmentation fault when 'set_time_limit' function is disabled + - Fixed issue #2035: Xdebug block everything with localhost in XAMMP + + + + 2021-10-04 + + + 3.1.0 + 3.1.0 + + + stable + stable + + BSD style + +Mon, Oct 04, 2021 - Xdebug 3.1.0 + += Fixed bugs: + + - Fixed issue #1472: Add assignments to computer readable trace format + - Fixed issue #1537: Add links to documentation to various different "features" after wizard has run + - Fixed issue #1738: Add xdebug_notify() function to send data through DBGp to a debugging client + - Fixed issue #1853: Enable profile compression for cachegrind files + - Fixed issue #1890: Add connected client and protocol features to diagnostic page + - Fixed issue #1898: API for querying the currently active mode(s) + - Fixed issue #1933: Allow for cloud ID to be set through the trigger + - Fixed issue #1938: Branches in traits aren't marked as executed + - Fixed issue #1948: Do not redirect warning and error messages to PHP's error log if an Xdebug log is active + - Fixed issue #1949: private properties for internal classes can't be fetched for debugging + - Fixed issue #1963: php exit code = -1073741819 when xdebug.mode = off (Windows Thread Safe Only) + - Fixed issue #1969: Provide breakpoint ID / info in DBGp run command responses + - Fixed issue #1970: xdebug_get_function_stack with unnamed (internal) parameters have wrong index + - Fixed issue #1972: Add support for PHP 8.1 Fibers + - Fixed issue #1974: Add gzip support to trace files + - Fixed issue #1976: Switch debug session cookie to Lax, and remove expiry time + - Fixed issue #1978: Xdebug's log messages are cut off at 512 bytes + - Fixed issue #1980: PHP 8.1: Mark enum classes as "enum" + - Fixed issue #1986: Add support for multiple trigger values + - Fixed issue #1989: Profiling does not output correct class when parent keyword is used + - Fixed issue #1992: Code Coverage with filter produces Segmentation fault on xdebug_stop_code_coverage() + - Fixed issue #1993: eval-ing broken code causes stepping to break + - Fixed issue #1996: Add support for Closure visualisation in traces, debugging, and Xdebug's var_dump + - Fixed issue #1997: Added xdebug_connect_to_client() to attempt a debugging connect while running code + - Fixed issue #1998: Double facet attribute generated for enums that are stored in properties + - Fixed issue #1999: Add "readonly" facet to PHP 8.1 readonly properties + - Fixed issue #2001: Add 'xdebug.use_compression' setting to turn on/off compression for profiling files + - Fixed issue #2004: Figure out what "XDEBUG_SHOW_FNAME_TODO" define is for + - Fixed issue #2007: xdebug 3.x fails to build on OS X 10.11 or earlier due to clock_gettime_nsec_np requirement + - Fixed issue #2008: Using the XDEBUG_SESSION cookie could bypass shared-secret checks + - Fixed issue #2009: xdebug_stop_code_coverage's argument has type mismatch + - Fixed issue #2011: Closures as protected properties have double facet XML attribute + - Fixed issue #2013: Support PHP 8.1 + - Fixed issue #2018: zlib compression support on Windows + - Fixed issue #2019: Xdebug crash because of uninitialized memory + - Fixed issue #2020: segfault if xdebug.dump.GET=* and integer key without value in URL + - Fixed issue #2021: Segmentation fault due to NULL bytes in internal anonymous class names + - Fixed issue #2025: Anonymous classes which extend are not detected as anonymous classes since PHP 8.0 + + + + 2021-09-07 + + + 3.1.0beta2 + 3.1.0beta2 + + + beta + beta + + BSD style + +Tue, Sep 07, 2021 - Xdebug 3.1.0beta2 + += Fixed bugs: + - This is a packaging fix only release. The package missed a file that were needed + for building on PHP 7.2 and 8.1. + + + + 2021-09-05 + + + 3.1.0beta1 + 3.1.0beta1 + + + beta + beta + + BSD style + +Sun, Sep 05, 2021 - Xdebug 3.1.0beta1 + ++ New features: + + - Fixed issue #1738: Add xdebug_notify() function to send data through DBGp to a debugging client + - Fixed issue #1853: Enable profile compression for cachegrind files + - Fixed issue #1898: API for querying the currently active mode(s) + - Fixed issue #1972: Add support for PHP 8.1 Fibers + - Fixed issue #1974: Add gzip support to trace files + - Fixed issue #1997: Added xdebug_connect_to_client() to attempt a debugging connect while running code + - Fixed issue #2001: Add 'xdebug.use_compression' setting to turn on/off compression for profiling files + - Fixed issue #2013: Support PHP 8.1 + ++ Improvements: + + - Fixed issue #1472: Add assignments to computer readable trace format + - Fixed issue #1890: Add connected client and protocol features to diagnostic page + - Fixed issue #1933: Allow for cloud ID to be set through the trigger + - Fixed issue #1969: Provide breakpoint ID / info in DBGp run command responses + - Fixed issue #1976: Switch debug session cookie to Lax, and remove expiry time + - Fixed issue #1980: PHP 8.1: Mark enum classes as "enum" + - Fixed issue #1986: Add support for multiple trigger values + - Fixed issue #1996: Add support for Closure visualisation in traces, debugging, and Xdebug's var_dump + - Fixed issue #1999: Add "readonly" facet to PHP 8.1 readonly properties + += Fixed bugs: + + - Fixed issue #1938: Branches in traits aren't marked as executed + - Fixed issue #1948: Do not redirect warning and error messages to PHP's error log if an Xdebug log is active + - Fixed issue #1949: private properties for internal classes can't be fetched for debugging + - Fixed issue #1963: php exit code = -1073741819 when xdebug.mode = off (Windows Thread Safe Only) + - Fixed issue #1970: xdebug_get_function_stack with unnamed (internal) parameters have wrong index + - Fixed issue #1978: Xdebug's log messages are cut off at 512 bytes + - Fixed issue #1989: Profiling does not output correct class when parent keyword is used + - Fixed issue #1992: Code Coverage with filter produces Segmentation fault on xdebug_stop_code_coverage() + - Fixed issue #1993: eval-ing broken code causes stepping to break + - Fixed issue #1998: Double facet attribute generated for enums that are stored in properties + - Fixed issue #2004: Figure out what "XDEBUG_SHOW_FNAME_TODO" define is for + - Fixed issue #2008: Using the XDEBUG_SESSION cookie could bypass shared-secret checks + - Fixed issue #2009: xdebug_stop_code_coverage's argument has type mismatch + - Fixed issue #2011: Closures as protected properties have double facet XML attribute + ++ Documentation + + - Fixed issue #1537: Add links to documentation to various different "features" after wizard has run + + + + 2021-04-08 + + + 3.0.4 + 3.0.4 + + + stable + stable + + BSD style + +Thu, Apr 08, 2021 - Xdebug 3.0.4 + += Fixed bugs: + + - Fixed issue #1802: Improve xdebug.org home page + - Fixed issue #1944: tracing is started without trigger, when profiler is also enabled + - Fixed issue #1947: xdebug_info() settings section does not show the modes that are overridden by XDEBUG_MODE + - Fixed issue #1950: Assignment trace with ASSIGN_OBJ_REF crashes + - Fixed issue #1954: Calling xdebug_start_trace without mode including tracing results in a fatal error + + + + 2021-02-22 + + + 3.0.3 + 3.0.3 + + + stable + stable + + BSD style + +Mon, Feb 22, 2021 - Xdebug 3.0.3 + += Fixed bugs: + + - Fixed issue #1930: No local variables with trigger and xdebug_break() + - Fixed issue #1931: xdebug_info() output misses configuration settings if phpinfo() has been called + - Fixed issue #1932: One line in multi-line string concatenation is not covered + - Fixed issue #1940: Wrong type used for showing GC Stats reports + + + + 2021-01-04 + + + 3.0.2 + 3.0.2 + + + stable + stable + + BSD style + +Mon, Jan 04, 2021 - Xdebug 3.0.2 + += Fixed bugs: + + - Fixed issue #1907: Empty exception message when setting the $message property to a stringable object + - Fixed issue #1910: Code coverage misses constructor property promotion code + - Fixed issue #1914: Compillation failure on OpenBSD + - Fixed issue #1915: Debugger should only start with XDEBUG_SESSION and not XDEBUG_PROFILE + - Fixed issue #1918: Warn if PHP's Garbage Collection is disabled in gc_stats mode + - Fixed issue #1919: Crash when enabling filter without the right mode active + - Fixed issue #1921: Xdebug does not start step debugging if start_with_request=trigger + - Fixed issue #1922: Code coverage misses array assignment lines + - Fixed issue #1924: Deprecated INI settings displayed in phpinfo() + - Fixed issue #1925: xdebug.start_with_request and start_upon_error display inconsistent values + - Fixed issue #1926: Add Xdebug mode's source to xdebug_info() output + - Fixed issue #1927: Crash when calling xdebug_stop_trace without a trace in progress + - Fixed issue #1928: xdebug_stop_gcstats() can also return false + + + + 2020-12-04 + + + 3.0.1 + 3.0.1 + + + stable + stable + + BSD style + +Fri, Dec 4, 2020 - xdebug 3.0.1 + += Fixed bugs: + + - Fixed issue #1893: Crash with ext-fiber and xdebug.mode=coverage + - Fixed issue #1896: Segfault with closures that are not created from user code + - Fixed issue #1897: Crash when removing a breakpoint + - Fixed issue #1900: Update README and add run-xdebug-tests.php to package + - Fixed issue #1901: Stack traces are shown (with a broken time) when Xdebug's mode includes 'debug' but not 'develop' or 'trace' + - Fixed issue #1902: Compillation failure on AIX + - Fixed issue #1903: Constants should always be available, regardless of which mode Xdebug is in + - Fixed issue #1904: Profile and trace files using %t or %u do not get the right names + - Fixed issue #1905: Debugger does not disable request timeouts + + + + 2020-11-25 + + + 3.0.0 + 3.0.0 + + + stable + stable + + BSD style + +Wed, Nov 25, 2020 - xdebug 3.0.0 + +Xdebug 3 includes major changes in functionality compared to Xdebug 2. The +primary way how you turn on functionality is through the new xdebug.mode PHP +configuration setting. This made it possible to massively increase performance +in many of Xdebug's sub systems as Xdebug is now much more conservative in +which hooks are enabled. + +Configuration changes, massive performance improvements, and PHP 8 support are +the primary features in Xdebug 3, but there is much more. The upgrade guide +lists the changes in great detail, please read it: + +https://xdebug.org/docs/upgrade_guide + +------------- + ++ New features: + + - Implemented issue #1762: Introduce feature modes + - Implemented issue #1793: Add xdebug.start_upon_error setting to cover the removed xdebug.remote_mode=jit feature. + - Implemented issue #1797: Implement generic logging + - Implemented issue #1801: Rename mode 'display' to mode 'develop' + - Implemented issue #1831: Add diagnostics function xdebug_info() + - Implemented issue #1833: Add links to documentation in diagnostic log + - Implemented issue #1837: Support for associative variadic variable names (PHP 8) + - Implemented issue #1841: Add support for PHP 8 'match' keyword + ++ Improvements: + + - Implemented issue #1680: Update var dumping routines to include relevant information for interned strings and immutable arrays + - Implemented issue #1712: Add unit to profiler data types + - Implemented issue #1743: Figuring out whether a call is a closure uses string comparisions instead of checking the ACC flag (Benjamin Eberlei) + - Implemented issue #1752: Use a stack pool to manage stack entries instead of allocating and deallocating entries + - Implemented issue #1755: Overload pcntl_fork() to prevent performance degradation by calling xdebug_get_pid often (Carlos Granados) + - Implemented issue #1781: Include 'Xdebug' in max nesting level error message + - Implemented issue #1783: Stacktrace needs vertical scrolling on small screens (Tobias Tom) + - Implemented issue #1789: Provide PHP stubs for Xdebug's functions + - Implemented issue #1807: Document Xdebug installation with yum and apt + - Implemented issue #1813: Make sure that the xdebug_init_*_globals don't do more than they need to, and that init is only done when xdebug.mode != off + - Implemented issue #1817: Switch filename storage from char*/size_t to zend_string* + - Implemented issue #1818: Switch variable storage from char*/size_t to zend_string* + - Implemented issue #1820: Increase time tracing precision (Michael Vorisek) + - Implemented issue #1824: Allow Xdebug's mode to be set through an environment variable + - Implemented issue #1825: Improve profiler performance by not calling fflush after every function (Michael Vorisek) + - Implemented issue #1826: Reduce profiler memory allocation and call overhead + - Implemented issue #1829: Switch to 10ns profiler resolution (Michael Vorisek) + - Implemented issue #1832: If connect back host can not be contacted, fallback to remote_host/port + - Implemented issue #1858: Only open/close log if there is an actual message to log + - Implemented issue #1860: Allow xdebug.cloud_id to be set through an environment variable + - Implemented issue #1814: Don't obtain the current time when it's not needed + - Implemented issue #1835: Add current trace and profile file name, to diagnostic page + - Implemented issue #1885: Change xdebug.start_with_ settings to PHP_INI_SYSTEM|PHP_INI_PERDIR + - Implemented issue #1889: max_nesting_level should only trigger in "develop" mode + +- Removed features: + + - Implemented issue #1795: Deprecate PHP 7.1 support + + - Implemented issue #1786: Remove idekey value fallback to USER/USERNAME environment variable + - Implemented issue #1809: Remove "overload_var_dump" setting + - Implemented issue #1810: Remove collect_vars and xdebug_get_declared_vars() + - Implemented issue #1812: Remove show_mem_delta setting + - Implemented issue #1838: Remove collect_params setting, and always default it to "4" + - Implemented issue #1847: Remove xdebug.remote_cookie_expire_time setting + - Implemented issue #1016: Removed support for pause-execution (introduced in beta1) + - Implemented issue #1868: Remove xdebug_disable and xdebug_enabled + - Implemented issue #1883: Function xdebug_is_enabled has been removed + += Changes: + + - Implemented issue #1378: Unfortunate coupling of default_enable=1 and remote_mode=jit + - Implemented issue #1773: Replace all xdebug.*_output_dir settings with xdebug.output_dir + - Implemented issue #1785: Replace xdebug.remote_mode and xdebug.auto_trace with generic "start-with-request" setting + - Implemented issue #1791: Replace xdebug.*trigger*, xdebug.*trigger_value*, with xdebug.start_with_request=trigger and xdebug.trigger_value + - Implemented issue #1792: Change start_with_request=always/never to start_with_request=yes/no + - Implemented issue #1794: Replace the filter's blacklist/whitelist with exclude/include + - Implemented issue #1811: Remove xdebug.collect_includes setting and always include them + - Implemented issue #1843: Adjust XDEBUG_CONFIG checks, and document what can be set through it + - Implemented issue #1844: Add deprecation warning for removed and renamed configuration setting names + - Implemented issue #1845: Rename xdebug.remote_{host,port} to xdebug.client_{host,port} + - Implemented issue #1846: Rename setting xdebug.remote_timeout to xdebug.connect_timeout_ms + - Implemented issue #1848: Change default Xdebug port from 9000 to 9003 + - Implemented issue #1850: Change array variable output in tracing to use modern [] syntax + - Implemented issue #1856: Rename xdebug.remote_connect_back to xdebug.discover_client_host + - Implemented issue #1857: Rename xdebug.remote_addr_header to xdebug.client_discovery_header + += Fixed bugs: + + - Fixed issue #1608: XDEBUG_CONFIG env var make sessions automatically START ever (at least send the XDEBUG_SESSION cookie) + - Fixed issue #1726: Memory leaks spotted in various places in typical error code paths + - Fixed issue #1757: Pause-execution feature degrades performance + - Fixed issue #1864: Incompatibility with PCS and protobuf extensions + - Fixed issue #1870: XDEBUG_SESSION_START URL parameter does not override XDEBUG_SESSION cookie + - Fixed issue #1871: The "idekey" is not set when debugging is started through XDEBUG_SESSION cookie + - Fixed issue #1873: xdebug_info() segfaults if the diagnostic buffer is empty + - Fixed issue #1874: Incompatibility with protobuf extension + - Fixed issue #1875: Overflow with large amounts of elements for variadics + - Fixed issue #1878: Compilation failure: Socket options TCP_KEEPCNT and TCP_KEEPINTVL do not exist on Solaris 10 Sparc + - Fixed issue #1880: Bundled unit test tests/debugger/bug00886.phar misses to load phar extension + - Fixed issue #1887: Crash bug with xdebug_call_class and xdebug_call_file + - Fixed issue #1756: Php process won't exit after running connected to a client + - Fixed issue #1823: Profiler generates negative data for memory usage + - Fixed issue #1834: Return type must be bool in overloaded set_time_limit + - Fixed issue #1888: Make headers sticky in xdebug_info() output + ++ Documentation + + - Fixed issue #1865: Document XDEBUG_TRIGGER environment variable + - Fixed issue #1866: Document comma separated xdebug.mode values + - Fixed issue #1884: Document where Xdebug's settings can be set + - Fixed issue #1892: Document changed/removed ini settings in the upgrade guide with the links provided + + + + 2020-11-16 + + + 3.0.0RC1 + 3.0.0RC1 + + + beta + beta + + BSD style + +Mon, Nov 16, 2020 - xdebug 3.0.0RC1 + +This is a BETA release, and not ready for production environments. + +Xdebug 3 has many changes. Please read the upgrade guide at +https://3.xdebug.org/docs/upgrade_guide + +Xdebug 3 documentation is available at https://3.xdebug.org/docs + +------------- + ++ Improvements: + + - Implemented issue #1814: Don't obtain the current time when it's not needed + - Implemented issue #1885: Change xdebug.start_with_ settings to PHP_INI_SYSTEM|PHP_INI_PERDIR + +- Removed features: + + - Implemented issue #1016: Removed support for pause-execution (introduced in beta1) + - Implemented issue #1868: Remove xdebug_disable and xdebug_enabled + - Implemented issue #1883: Function xdebug_is_enabled has been removed + += Fixed bugs: + + - Fixed issue #1608: XDEBUG_CONFIG env var make sessions automatically START ever (at least send the XDEBUG_SESSION cookie) + - Fixed issue #1757: Pause-execution feature degrades performance + - Fixed issue #1864: Incompatibility with PCS and protobuf extensions + - Fixed issue #1870: XDEBUG_SESSION_START URL parameter does not override XDEBUG_SESSION cookie + - Fixed issue #1871: The "idekey" is not set when debugging is started through XDEBUG_SESSION cookie + - Fixed issue #1873: xdebug_info() segfaults if the diagnostic buffer is empty + - Fixed issue #1874: Incompatibility with protobuf extension + - Fixed issue #1875: Overflow with large amounts of elements for variadics + - Fixed issue #1878: Compilation failure: Socket options TCP_KEEPCNT and TCP_KEEPINTVL do not exist on Solaris 10 Sparc + - Fixed issue #1880: Bundled unit test tests/debugger/bug00886.phar misses to load phar extension + - Fixed issue #1887: Crash bug with xdebug_call_class and xdebug_call_file + ++ Documentation + + - Fixed issue #1865: Document XDEBUG_TRIGGER environment variable + - Fixed issue #1866: Document comma separated xdebug.mode values + - Fixed issue #1884: Document where Xdebug's settings can be set + + + + 2020-10-14 + + + 3.0.0beta1 + 3.0.0beta1 + + + beta + beta + + BSD style + +Wed, Oct 14, 2020 - xdebug 3.0.0beta1 + +This is a BETA release, and not ready for production environments. + +Xdebug 3 has many changes. Please read the upgrade guide at +https://3.xdebug.org/docs/upgrade_guide + +Xdebug 3 documentation is available at https://3.xdebug.org/docs + +------------- + ++ New features: + + - Implemented issue #1762: Introduce feature modes + - Implemented issue #1793: Add xdebug.start_upon_error setting to cover the removed xdebug.remote_mode=jit feature. + - Implemented issue #1797: Implement generic logging + - Implemented issue #1801: Rename mode 'display' to mode 'develop' + - Implemented issue #1831: Add diagnostics function xdebug_info() + - Implemented issue #1833: Add links to documentation in diagnostic log + - Implemented issue #1837: Support for associative variadic variable names (PHP 8) + - Implemented issue #1841: Add support for PHP 8 'match' keyword + +- Removed features: + + - Implemented issue #1795: Deprecate PHP 7.1 support + + - Implemented issue #1786: Remove idekey value fallback to USER/USERNAME environment variable + - Implemented issue #1809: Remove "overload_var_dump" setting + - Implemented issue #1810: Remove collect_vars and xdebug_get_declared_vars() + - Implemented issue #1812: Remove show_mem_delta setting + - Implemented issue #1838: Remove collect_params setting, and always default it to "4" + - Implemented issue #1847: Remove xdebug.remote_cookie_expire_time setting + += Changes: + + - Implemented issue #1378: Unfortunate coupling of default_enable=1 and remote_mode=jit + - Implemented issue #1773: Replace all xdebug.*_output_dir settings with xdebug.output_dir + - Implemented issue #1785: Replace xdebug.remote_mode and xdebug.auto_trace with generic "start-with-request" setting + - Implemented issue #1791: Replace xdebug.*trigger*, xdebug.*trigger_value*, with xdebug.start_with_request=trigger and xdebug.trigger_value + - Implemented issue #1792: Change start_with_request=always/never to start_with_request=yes/no + - Implemented issue #1794: Replace the filter's blacklist/whitelist with exclude/include + - Implemented issue #1811: Remove xdebug.collect_includes setting and always include them + - Implemented issue #1844: Add deprecation warning for removed and renamed configuration setting names + - Implemented issue #1845: Rename xdebug.remote_{host,port} to xdebug.client_{host,port} + - Implemented issue #1846: Rename setting xdebug.remote_timeout to xdebug.connect_timeout_ms + - Implemented issue #1848: Change default Xdebug port from 9000 to 9003 + - Implemented issue #1850: Change array variable output in tracing to use modern [] syntax + - Implemented issue #1856: Rename xdebug.remote_connect_back to xdebug.discover_client_host + - Implemented issue #1857: Rename xdebug.remote_addr_header to xdebug.client_discovery_header + ++ Improvements: + + - Implemented issue #1680: Update var dumping routines to include relevant information for interned strings and immutable arrays + - Implemented issue #1712: Add unit to profiler data types + - Implemented issue #1743: Figuring out whether a call is a closure uses string comparisions instead of checking the ACC flag (Benjamin Eberlei) + - Implemented issue #1752: Use a stack pool to manage stack entries instead of allocating and deallocating entries + - Implemented issue #1755: Overload pcntl_fork() to prevent performance degradation by calling xdebug_get_pid often (Carlos Granados) + - Implemented issue #1781: Include 'Xdebug' in max nesting level error message + - Implemented issue #1783: Stacktrace needs vertical scrolling on small screens (Tobias Tom) + - Implemented issue #1789: Provide PHP stubs for Xdebug's functions + - Implemented issue #1807: Document Xdebug installation with yum and apt + - Implemented issue #1813: Make sure that the xdebug_init_*_globals don't do more than they need to, and that init is only done when xdebug.mode != off + - Implemented issue #1817: Switch filename storage from char*/size_t to zend_string* + - Implemented issue #1818: Switch variable storage from char*/size_t to zend_string* + - Implemented issue #1820: Increase time tracing precision (Michael Vorisek) + - Implemented issue #1824: Allow Xdebug's mode to be set through an environment variable + - Implemented issue #1825: Improve profiler performance by not calling fflush after every function (Michael Vorisek) + - Implemented issue #1826: Reduce profiler memory allocation and call overhead + - Implemented issue #1829: Switch to 10ns profiler resolution (Michael Vorisek) + - Implemented issue #1832: If connect back host can not be contacted, fallback to remote_host/port + - Implemented issue #1858: Only open/close log if there is an actual message to log + - Implemented issue #1860: Allow xdebug.cloud_id to be set through an environment variable + += Fixed bugs: + + - Fixed issue #1756: Php process won't exit after running connected to a client + - Fixed issue #1823: Profiler generates negative data for memory usage + - Fixed issue #1834: Return type must be bool in overloaded set_time_limit + + + + 2020-09-28 + + + 2.9.8 + 2.9.8 + + + stable + stable + + BSD style + +Mon, Sep 28, 2020 - xdebug 2.9.8 + += Fixed bugs: + + - Fixed issue #1851: Paths are not counted as coveraged with loops calling function + - Fixed issue #1855: Build issues on FreeBSD + + + + 2020-09-16 + + + 2.9.7 + 2.9.7 + + + stable + stable + + BSD style + +Wed, Sep 16, 2020 - xdebug 2.9.7 + += Fixed bugs: + + - Fixed issue #1839: Add keepalive options to debugging socket + + + + 2020-05-29 + + + 2.9.6 + 2.9.6 + + + stable + stable + + BSD style + +Fri, May 29, 2020 - xdebug 2.9.6 + += Fixed bugs: + + - Fixed issue #1782: Cookie "XDEBUG_SESSION" will be soon rejected because it has the "sameSite" attribute set to none + - Fixed issue #1787: Branch coverage data does not always follow the lines/functions format + - Fixed issue #1790: Segfault in var_dump() or while debugging with protobuf extension + + + + 2020-04-25 + + + 2.9.5 + 2.9.5 + + + stable + stable + + BSD style + +Sat, Apr 25, 2020 - xdebug 2.9.5 + += Fixed bugs: + + - Fixed issue #1772: Crash with exception thrown inside a destructor + - Fixed issue #1775: Segfault when another extension compiles a PHP file during RINIT + - Fixed issue #1779: Nested multi-line built-in function in namespace are not covered + + + + 2020-03-23 + + + 2.9.4 + 2.9.4 + + + stable + stable + + BSD style + +Mon, Mar 23, 2020 - xdebug 2.9.4 + += Fixed bugs: + + - Fixed issue #1763: Crash while setting opcode overrides in ZTS mode. + - Fixed issue #1766: Using the DBGp detach command disables remote debugging for the whole process. + + + + 2020-03-13 + + + 2.9.3 + 2.9.3 + + + stable + stable + + BSD style + +Fri, Mar 13, 2020 - xdebug 2.9.3 + += Fixed bugs: + + - Fixed issue #1753: Resolved breakpoints use information from wrong files + - Fixed issue #1758: Xdebug changes error_get_last results inside a try catch + - Fixed issue #1759: User registered opcode handlers should call ones already set by other extensions + + + + 2020-01-31 + + + 2.9.2 + 2.9.2 + + + stable + stable + + BSD style + +Fri, Jan 31, 2020 - xdebug 2.9.2 + += Fixed bugs: + + - Fixed issue #1735: DBGp eval warning promoted to Exception can cause out-of-sync responses + - Fixed issue #1736: Segmentation fault when other extensions run PHP in RINIT + - Fixed issue #1739: Tracing footer not written + + + + 2020-01-16 + + + 2.9.1 + 2.9.1 + + + stable + stable + + BSD style + +Thu, Jan 16, 2020 - xdebug 2.9.1 + += Fixed bugs: + + - Fixed issue #1721: Header may not contain NUL bytes in Unknown on line 0 + - Fixed issue #1727: Debugger stops more often than expected due to resolving breakpoints + - Fixed issue #1728: INIT_STATIC_METHOD_CALL is not overloaded + - Fixed issue #1731: var_dump with DateTime does not output properties (Ryan Mauger) + - Fixed issue #1733: SEND_VAR_NO_REF_EX opcode, used for require(), is not overloaded + - Fixed issue #1734: Segfault with DBGp "source" with a out-of-range start line number + + + + 2019-12-09 + + + 2.9.0 + 2.9.0 + + + stable + stable + + BSD style + +Mon, Dec 9, 2019 - xdebug 2.9.0 + ++ Improvements: + + - Fixed issue #1723: Class/function pre-analysis for code coverage speed improvements + +- Removed features: + + - Fixed issue #1301: Removed aggregated profiler feature + - Fixed issue #1720: Remove superfluous xdebug.remote_handler setting + += Fixed bugs: + + - Fixed issue #1722: Build warning issues on FreeBSD + - Fixed issue #1724: Missing property types and uninitialised values in variable dumping routines + + + + 2019-12-02 + + + 2.8.1 + 2.8.1 + + + stable + stable + + BSD style + +Mon, Dec 2, 2019 - xdebug 2.8.1 + += Fixed bugs: + + - Fixed issue #1717: Code coverage turned slow after update from 2.7.2 to 2.8.0 + + + + 2019-10-31 + + + 2.8.0 + 2.8.0 + + + stable + stable + + BSD style + +Thu, Oct 31, 2019 - xdebug 2.8.0 + += Fixed bugs: + + - Fixed issue #1665: Segfault with garbage collection and complex function arguments + - Fixed issue #1699: Crash during debugging Phalcon project + - Fixed issue #1705: Crash while debugging with ionCube being used + - Fixed issue #1708: Crash on evaluating object with properties + - Fixed issue #1709: Wrong data type breaks tests on Big Endian build + - Fixed issue #1713: INIT_FCALL is not overloaded in code coverage + + + + 2019-08-26 + + + 2.8.0beta2 + 2.8.0beta2 + + + beta + beta + + BSD style + +Mon, Aug 26, 2019 - xdebug 2.8.0beta2 + += Fixed bugs: + + - Fixed issue #1540: Code coverage should not run when turned off in php.ini + - Fixed issue #1573: Using an exception_handler creates an extra broken profiler file + - Fixed issue #1589: function names used in auto_prepend_file missing from profile file + - Fixed issue #1613: Wrong name displayed for Recoverable fatal error + - Fixed issue #1652: Problems with detach in debugger init stage + - Fixed issue #1676: Xdebug doesn't write trace footer for shutdown functions + - Fixed issue #1689: Traces show return values and exit information for functions without entry information + - Fixed issue #1691: Code Coverage misses fluent interface function call + - Fixed issue #1698: Switch PHP 7.4 Windows builds back to VS17 + - Fixed issue #1700: Xdebug abuses possibilty immutable class flags + + + + 2019-07-25 + + + 2.8.0beta1 + 2.8.0beta1 + + + beta + beta + + BSD style + +Thu, Jul 25, 2019 - xdebug 2.8.0beta1 + += Fixed bugs: + + - Fixed issue #1679: Code Coverage misses static property as function + argument + - Fixed issue #1682: Invalid NULL byte in debugger XML with anonymous classes + - Fixed issue #1683: Xdebug does not compile due to changes to ASSIGN_ADD and + friends operations in PHP 7.4alpha3 + - Fixed issue #1687: Use appropriate process ID for logging and "right + process" tracking + - Fixed issue #1688: Improve performance by using getpid() only when step + debugger is active + + + + 2019-06-28 + + + 2.8.0alpha1 + 2.8.0alpha1 + + + beta + beta + + BSD style + +Fri, May 28, 2019 - xdebug 2.8.0alpha1 + ++ Added features: + + - Implemented issue #1599: Add support for PHP 7.4 + ++ Improvements: + + - Implemented issue #1388: Support 'resolved' flag for breakpoints + - Implemented issue #1664: Run breakpoint resolver when after a new breakpoint is added as well + += Fixed bugs: + + - Fixed issue #1660: Return breakpoints for methods don't break immediately + +- Removed features: + + - Fixed issue #1666: Remove xdebug.extended_info setting + + + + 2019-05-06 + + + 2.7.2 + 2.7.2 + + + stable + stable + + BSD style + +Mon, May 6, 2019 - xdebug 2.7.2 + += Fixed bugs: + + - Fixed issue #1488: Rewrite DBGp 'property_set' to always use eval + - Fixed issue #1586: error_reporting()'s return value is incorrect during debugger's 'eval' command + - Fixed issue #1615: Turn off Zend OPcache when remote debugger is turned on + - Fixed issue #1656: remote_connect_back alters header if multiple values are present + - Fixed issue #1662: __debugInfo should not be used for user-defined classes + + + + 2019-04-05 + + + 2.7.1 + 2.7.1 + + + stable + stable + + BSD style + +Wed, Apr 5, 2019 - xdebug 2.7.1 + += Fixed bugs: + + - Fixed issue #1646: Missing newline in error message + - Fixed issue #1647: Memory corruption when a conditional breakpoint is used + - Fixed issue #1641: Perfomance degradation with getpid syscall (Kees Hoekzema) + + + + 2019-03-06 + + + 2.7.0 + 2.7.0 + + + stable + stable + + BSD style + +Wed, Mar 6, 2019 - xdebug 2.7.0 + += Fixed bugs: + + - Fixed issue #1520: Xdebug does not handle variables and properties with "-" in their name + - Fixed issue #1577: Code coverage path analysis with chained catch fails in PHP 7.3 + - Fixed issue #1639: Compile warning/error on GCC 8 or Clang due to "break intentionally missing" + - Fixed issue #1642: Debugger gives: "Warning: Header may not contain NUL bytes" + + + + 2019-02-15 + + + 2.7.0RC2 + 2.7.0RC2 + + + beta + beta + + BSD style + +Fri, Feb 15, 2019 - xdebug 2.7.0RC2 + += Fixed bugs: + + - Fixed issue #1551: Property with value null is not represented well + - Fixed issue #1621: Xdebug fails to compile cleanly on 32-bit platforms + - Fixed issue #1625: Work around ABI conflicts in PHP 7.3.0/PHP 7.3.1 + - Fixed issue #1628: The PHP function name being constructed to record when GC Collection runs, is not freed + - Fixed issue #1629: SOAP Client/Server detection code does not handle inherited classes + + + + 2019-02-01 + + + 2.7.0RC1 + 2.7.0RC1 + + + beta + beta + + BSD style + +Fri, Feb 1, 2019 - xdebug 2.7.0RC1 + += Fixed bugs: + + - Fixed issue #1571: File/line information is not shown for closures in namespaces. + - Fixed issue #1578: Compile error due to redefinition of "zif_handler" with old GCCs. + - Fixed issue #1583: Xdebug crashes when OPcache's compact literals optimisation is on. + - Fixed issue #1598: Make path/branch coverage work with OPcache loaded for PHP 7.3 and later. + - Fixed issue #1620: Division by zero when GC Stats Collection runs with memory manager disabled. + + + + 2018-09-20 + + + 2.7.0beta1 + 2.7.0beta1 + + + beta + beta + + BSD style + +Thu, Sep 20, 2018 - xdebug 2.7.0beta1 + ++ Improvements: + + - Fixed issue #1519: PHP 7.3 support + + + + 2018-04-01 + + + 2.7.0alpha1 + 2.7.0alpha1 + + + beta + beta + + BSD style + +Sun, Apr 1, 2018 - xdebug 2.7.0alpha1 + += Improvements: + + - Fixed issue #938: Support remote debugging for PHP scripts that fork. (Sponsored by Brad Wilson) + - Fixed issue #1487: Re-enable IPv6 test on Travis. + += Fixed bugs: + + - Fixed issue #1526: Namespace filter does equality match instead of prefix match. + - Fixed issue #1532: SIGABRT when using remote debugging and an error is thrown in eval(). + - Fixed issue #1543: Various memory leaks due to changes in (internal) string handling. + + + + 2018-08-01 + + + 2.6.1 + 2.6.1 + + + stable + stable + + BSD style + +Wed, Aug 1, 2018 - xdebug 2.6.1 + += Fixed bugs: + + - Fixed issue #1525: Namespace filter does equality match instead of prefix match + - Fixed issue #1532: SIGABRT when using remote debugging and an error is thrown in eval() (Philip Hofstetter) + - Fixed issue #1543: Various memory leaks due to changes in (internal) string handling + - Fixed issue #1556: Crash when register_shutdown_function() is called with a function named call_user_func* + - Fixed issue #1557: Remove 'return' in void xdebug_build_fname + - Fixed issue #1568: Can't debug object properties that have numeric keys + ++ Improvements: + + - Fixed issue #1487: Re-enable IPv6 test on Travis + + + + 2018-01-29 + + + 2.6.0 + 2.6.0 + + + stable + stable + + BSD style + +Mon, Jan 29, 2018 - xdebug 2.6.0 + += Fixed bugs: + + - Fixed issue #1522: Remote debugging test failures on s390 (Big Endian). + + + + 2018-01-23 + + + 2.6.0RC2 + 2.6.0RC2 + + + beta + beta + + BSD style + +Tue, Jan 23, 2018 - xdebug 2.6.0RC2 + += Fixed bugs: + + - Fixed issue #1521: xdebug_gc_stats.* missing from 2.6.0RC1 tarball + + + + 2018-01-22 + + + 2.6.0RC1 + 2.6.0RC1 + + + beta + beta + + BSD style + +Mon, Jan 22, 2018 - xdebug 2.6.0RC1 + ++ Added features: + + - Fixed issue #1506: Add garbage collection statistics feature (Benjamin Eberlei). + - Fixed issue #1507: Add functions to access Zend Engine garbage collection metrics (Benjamin Eberlei). + ++ Improvements: + + - Fixed issue #1510: Change switch/case "break intentionally missing" comments to use GCC 7's new "fallthrough" attribute. + - Fixed issue #1511: Detect and use compiler flags through new configure option. + += Fixed bugs: + + - Fixed issue #1335: Debugging with PhpStorm sometimes gives "can not get property". + - Fixed issue #1454: Invalid memory read or segfaults from a __call() method. + - Fixed issue #1508: Code coverage filter not checked in xdebug_common_assign_dim handler. + - Fixed issue #1509: Code coverage missing for case inside switch with PHP 7.2. + - Fixed issue #1512: Xdebug does not properly encode and escape properties with quotes and \0 characters. + - Fixed issue #1514: Variable names with a NULL char are cut off at NULL char. + - Fixed issue #1515: Object property names with a NULL char are cut off at NULL char. + - Fixed issue #1516: Can't fetch variables or object properties which have \0 characters in them. + - Fixed issue #1517: Notifications incorrectly specify the error type in "type_string" instead of "type". + + + + 2017-12-28 + + + 2.6.0beta1 + 2.6.0beta1 + + + beta + beta + + BSD style + +Thu, Dec 28, 2017 - xdebug 2.6.0beta1 + ++ Added features: + + - Fixed issue #1059: Add filter capabilities to tracing, stack traces, and code coverage. + - Fixed issue #1437: Add X-Profile-File-Name header when a profile file has been generated. + ++ Improvements: + + - Fixed issue #1493: Run test suite in AppVeyor for Windows CI. + - Fixed issue #1498: Use new ZEND_EXTENSION API in config.w32 build scripts. (Kalle) + += Fixed bugs: + + - Fixed issue #702: Check whether variables tracing also works with =&. + - Fixed issue #1501: Xdebug var dump tries casting properties. + - Fixed issue #1502: SEND_REF lines are not marked as covered. + + + + 2017-12-02 + + + 2.6.0alpha1 + 2.6.0alpha1 + + + beta + beta + + BSD style + +Sat, Dec 2, 2017 - xdebug 2.6.0alpha1 + ++ Added features: + + - Implemented issue #474: Added "memory" output to profiling files, to find out where memory is allocated. + - Implemented issue #575: Dump super globals contents to error log upon errors, just like when this would happen for stack traces. + - Implemented issue #964: Parse X-Forwarded-For for the first IP address when selecting the remote_connect_back host (Steve Easley). + - Implemented issue #990: Add DBGp: notifications for notices and warnings to be shown in IDEs. + - Implemented issue #1312: Implement extended_properties feature to remote debugging to support names and values with low ASCII values. + - Implemented issue #1323: Added xdebug.filename_format setting to configure the formatting of filenames when tracing. + - Implemented issue #1379: Added support for Unix domain sockets to xdebug.remote_host (Sara Golemon). + - Implemented issue #1380: Added xdebug_is_debugger_active() that returns true when debugger is connected. + - Implemented issue #1391: Added support for earlier stack frames through new argument for xdebug_call_* functions. + - Implemented issue #1420: Handle PHP 7.2's new methods for switch/case + - Implemented issue #1470: Added xdebug.remote_timeout to make connect timeout configurable. + - Implemented issue #1495: Make var_dump() also use the new xdebug.filename_format when formatting filenames. + ++ Improvements: + + - Implemented issue #847: Added support for "%s" specifier for xdebug.trace_output_name. + - Implemented issue #1384: Compile warning on Ubuntu 16.04 with GCC 5.4.x. + - Implemented issue #1401: Improved error message in case the connection breaks. + - Implemented issue #1430: Change DBGp tests to use TEST_PHP_EXECUTABLE instead of hard coded 'php' + - Implemented issue #1484: Use FD_CLOEXEC with debugging sockets to prevent FDs from leaking to forked processes (Chris Wright). + - Improve the foldexpr in xt.vim to fold lines correctly (Donie Leigh). + += Fixed bugs: + + - Fixed issue #1272: property_get doesn't return @attributes for SimpleXMLElement. + - Fixed issue #1305: Property names with quotes can not be fetch while debugging. + - Fixed issue #1431: Fix "use after free" with in add_name_attribute_or_element. + - Fixed issue #1432: Fixed memory leak with xdebug_path_info_dtor. + - Fixed issue #1449: Debugging breaks with array element keys containing low-ASCII variables. + - Fixed issue #1471: Tracing crashes with return_assignments and ternairy operator. + - Fixed issue #1474: Crashes due to variable resolving/reading mechanism not taking care of temporary hash tables correctly (Nikita Popov, Derick). + - Fixed issue #1481: Fixed s390x and ppc64 builds (Remi Collet). + - Fixed issue #1486: Crash on ZEND_SWITCH_LONG / ZEND_SWITCH_STRING with more than 32 cases. + - Fixed issue #1496: Rewrite README.rst to be more clear on how to install and build Xdebug. + +~ Changes: + + - Fixed issue #1411: Use Error (Throwable) instead of fatal error when maximum nesting level is reached. + +- Removed features: + + - Implemented issue #1377: Drop support for PHP 5.5 and 5.6, only PHP 7 is now supported + + + + 2017-06-21 + + + 2.5.5 + 2.5.5 + + + stable + stable + + BSD style + += Fixed bugs: + + - Fixed issue #1439: TYPE_CHECK needs overloading due to smart branches + - Fixed issue #1444: Code Coverage misses a variable in a multi-line function + call + - Fixed issue #1446: Code Coverage misses elseif if it uses an isset with a + property + + + + 2017-05-15 + + + 2.5.4 + 2.5.4 + + + stable + stable + + BSD style + +Mon, May 15, 2017 - xdebug 2.5.4 + += Fixed bugs: + + - Fixed issue #799: Function traces report base class instead of object name + - Fixed issue #1421: Fix set_time_limit hanging on PHP 5.6 when pcntl_exec + does not exist (Frode E. Moe) + - Fixed issue #1429: Code coverage does not cover null coalesce + - Fixed issue #1434: Code coverage segfaults on 32-bit arch + + + + 2017-04-18 + + + 2.5.3 + 2.5.3 + + + stable + stable + + BSD style + +Mon, Apr 18, 2017 - xdebug 2.5.3 + += Fixed bugs: + + - Fixed issue #1421: Xdebug crashes when it is loaded without pcntl being + present + + + + 2017-04-17 + + + 2.5.2 + 2.5.2 + + + stable + stable + + BSD style + +Mon, Apr 17, 2017 - xdebug 2.5.2 + += Fixed bugs: + + - Fixed issue #701: Functions as array indexes show ??? in trace + - Fixed issue #1403: Code coverage does not cover BIND_STATIC + - Fixed issue #1404: Execution time is calculated incorrectly + - Fixed issue #1413: Code coverage mishap with PHP 7.1.3 + - Fixed issue #1414: Missing variable assignment in traces with OPcache + loaded + - Fixed issue #1415: Crash with multiple catch constructs with OPcache loaded + - Fixed issue #1416: Trace files should not include the first result of a + generator if it hasn't started yet + - Fixed issue #1417: Fetching properties of static class contexts fails due + to incorrect fetch mode + - Fixed issue #1419: Summary not written when script ended with + "pcntl_exec()" + + + + 2017-04-17 + + + 2.5.2 + 2.5.2 + + + stable + stable + + BSD style + +Sun, Feb 26, 2017 - xdebug 2.5.1 + += Fixed bugs: + + - Fixed issue #1057: Add xdebug.ini of all settings to package + - Fixed issue #1165: DBGp: step_out skips subsequent function calls + - Fixed issue #1180: Code coverage crashes with non-standard start/stops + - Fixed issue #1278: Xdebug with PHP 7 does not handle prefill-from-oparray + for XDEBUG_CC_UNUSED + - Fixed issue #1300: Xdebug functions are not exposing their signature to + Reflection + - Fixed issue #1313: Arguments to __call() trampoline picked from the wrong + memory location + - Fixed issue #1329: While printing out a stack with and function parameters, + XDebug reads uninitialized zvals or free()d memory + - Fixed issue #1381: Code Coverage misses line due to missing FETCH_DIM_W + overload + - Fixed issue #1385: can not fetch IS_INDIRECT properties + - Fixed issue #1386: Executable code not shown as executed/executable + - Fixed issue #1392: Unable to compile on FreeBSD due to missing struct + definition + - Fixed issue #1394: Code coverage does not cover instanceof (in elseif) + + + + 2016-12-04 + + + 2.5.0 + 2.5.0 + + + stable + stable + + BSD style + +Sun, Dec 4, 2016 - xdebug 2.5.0 + ++ Added features: + + - Implemented issue #1232: add memory delta to HTML traces + - Implemented issue #1365: Allow remote_connect_back to be set through + XDEBUG_CONFIG + += Fixed bugs: + + - Fixed issue #1168: Added defensive check to prevent infinite loop + - Fixed issue #1242: Xdebug on Windows with Eclipse has issues with + breakpoint IDs + - Fixed issue #1343: Wrong values of numerical keys outside 32bit range + - Fixed issue #1357: Function signature using variadics is reported as being + not executed + - Fixed issue #1361: Remote debugging connection issues with Windows (Anatol + Belski) + - Fixed issue #1373: Crash in zend_hash_apply_with_arguments when debugging, + due to unset symbol table + + + + 2016-11-12 + + + 2.5.0RC1 + 2.5.0RC1 + + + stable + stable + + BSD style + +Sat, Nov 12, 2016 - xdebug 2.5.0RC1 + ++ Added features: + + - Implemented issue #998: Added support for IPv6 (Thomas Vanhaniemi) + - Implemented issue #1297: Initial PHP 7.1 support + += Fixed bugs: + + - Fixed issue #1295: Apache crashes (SIGSEGV) when trying to establish + connection when sockfd is large + - Fixed issue #1303: POLLRDHUP is not supported outside of Gnu/Linux + - Fixed issue #1331: Segfault in code coverage + +- Removed features: + + - Support for PHP versions lower than PHP 5.5 has been dropped + + + + 2016-08-02 + + + 2.4.1 + 2.4.1 + + + stable + stable + + BSD style + +Tue, Aug 02, 2016 - xdebug 2.4.1 + += Fixed bugs: + + - Fixed issue #1106: A thrown Exception after a class with __debugInfo gives + 2 errors + - Fixed issue #1241: FAST_CALL/FAST_RET take #2 + - Fixed issue #1246: Path and branch coverage should be initialised per + request, not globally + - Fixed issue #1263: Code coverage segmentation fault with opcache enabled + - Fixed issue #1277: Crash when using a userland function from RSHUTDOWN with + profiling enabled + - Fixed issue #1282: var_dump() of integers > 32 bit is broken on Windows + - Fixed issue #1288: Segfault when uncaught exception message does not + contain " in " + - Fixed issue #1291: Debugclient installation fails on Mac OS X + - Fixed issue #1326: Tracing and generators crashes with PHP 7.x + - Fixed issue #1333: Profiler accesses memory structures after freeing + + + + 2016-01-25 + + + 2.4.0RC4 + 2.4.0RC4 + + + beta + beta + + BSD style + +Mon, Jan 25, 2016 - xdebug 2.4.0RC4 + += Fixed bugs: + + - Fixed issue #1220: Segmentation fault if var_dump() output is too large. + - Fixed issue #1223: Xdebug crashes on PHP 7 when doing a DBGp eval command. + - Fixed issue #1229: Issues with GCC 4.8, which in -O2 move removes some + required code. + - Fixed issue #1235: Xdebug does not compile against PHP 7.1-dev due to + ZEND_FETCH_STATIC_PROP*. + - Fixed issue #1236: Can't remove breakpoints with negative IDs. + - Fixed issue #1238: Xdebug crashes with SIGSEGV while enumerating references + in variables. + - Fixed issue #1239: Crash due to changes with the CATCH opcode's jump + mechanism in 7.1 + - Fixed issue #1241: Xdebug doesn't handle FAST_RET and FAST_CALL opcodes for + branch/dead code analysis, and path coverage. + - Fixed issue #1245: xdebug_dump_superglobals dumps *uninitialized* with PHP + 7. + - Fixed issue #1250: Add PHP version descriptors to debugging log and profile + files. + + + + 2016-03-03 + + + 2.4.0 + 2.4.0 + + + stable + stable + + BSD style + +Thu, Mar 03, 2016 - xdebug 2.4.0 + += Fixed bugs: + + - Fixed issue #1258: Case in PHP 7.0 and code coverage + - Fixed issue #1261: segmentation fault in xdebug.so with PHP 7.0 version of + 'pkgtools' due to spl_autoload() + - Fixed issue #1262: overload_var_dump=0 messes with xdebug_var_dump() + - Fixed issue #1266: xdebug_dump_superglobals() always dumps empty stack on + PHP 7 + - Fixed issue #1267: AIX build issues + - Fixed issue #1270: String parsing marked not covered with PHP 7 + + + + 2015-12-12 + + + 2.4.0RC3 + 2.4.0RC3 + + + beta + beta + + BSD style + +Wed, Dec 12, 2015 - xdebug 2.4.0RC3 + += Fixed bugs: + + - Fixed issue #1221: Sort out Windows x64 PHP 7 support + - Fixed issue #1229: Detect GCC 4.8 and disable optimisations when it is found + += Others: + + - Made the test suite work for Windows too. Finally, after 13 years. + + + + 2015-12-02 + + + 2.4.0RC2 + 2.4.0RC2 + + + beta + beta + + BSD style + +Wed, Dec 02, 2015 - xdebug 2.4.0RC2 + += Fixed bugs: + + - Fixed issue #1181: Remote debugging does not handle exceptions after using + zend_read_property + - Fixed issue #1189: Remove address attribute from remote debugging responses + - Fixed issue #1194: The error message is doubly HTML-encoded with assert() + - Fixed issue #1210: Segfault with code coverage dead code analysis and + foreach on PHP 7 + - Fixed issue #1215: SIGSEGV if xdebug.trace_output_dir directory does not + exist + - Fixed issue #1217: xdebug.show_error_trace should not be enabled by default + - Fixed issue #1218: Xdebug messes with the exception code, by casting it to + int + - Fixed issue #1219: Set default value for xdebug.overload_var_dump to 2 to + include file / line numbers by default + - Use long for PHP 5, and zend_long for PHP 7 for ini settings in the globals + + + + 2015-11-21 + + + 2.4.0RC1 + 2.4.0RC1 + + + beta + beta + + BSD style + +Sat, Nov 21, 2015 - xdebug 2.4.0RC1 + += Fixed bugs: + + - Fixed issue #1195: Segfault with code coverage and foreach + - Fixed issue #1200: Additional opcodes need to be overloaded for PHP 7 + - Fixed issue #1202: Anonymous classes are not handled properly while remote debugging + - Fixed issue #1203: Accessing static property of a class that has no static properties crashes while remote debugging + - Fixed issue #1209: Segfault with building a function name for create_function + - Restored Windows support (Includes patches by Jan Ehrhardt) + + + + 2015-11-05 + + + 2.4.0beta1 + 2.4.0beta1 + + + beta + beta + + BSD style + +Thu, Sep 05, 2015 - xdebug 2.4.0beta1 + ++ Added features: + + - Implemented issue #1109: Added support for PHP 7. + - Implemented issue #1153: Add function monitor functionality. + - Implemented issue #1183: Add xdebug.show_error_trace setting to + allow/disallow to show a stack trace for every Error (throwable) + += Fixed bugs: + + - Fixed issue #1070: Too many open files error with php-fpm: connections not + closed. (Patch by Sean Dubois) + - Fixed issue #1123: With Xdebug 2.3.1, PHPUnit with coverage is + exponentially slower than without + - Fixed issue #1166: Using $this in __debugInfo() causes infinite recursion + - Fixed issue #1173: Segmentation fault in xdebug_get_monitored_functions() + - Fixed issue #1182: Using PHPStorm with PHP 7 RC1 and xdebug 2.4-dev break + points are passed by including setting break point at start of script + - Fixed issue #1192: Dead code analysis does not work for generators with + 'return;' + + + + 2015-06-19 + + + 2.3.3 + 2.3.3 + + + stable + stable + + BSD style + +Fri, Jun 19, 2015 - xdebug 2.3.3 + += Fixed bugs: + + - Fixed issue #1130: Escaping issues with docrefs and HTML characters in + error messages + - Fixed issue #1133: PDO exception code value type is changed + - Fixed issue #1137: Windows does not support %zu formatting for sprintf + - Fixed issue #1140: Tracing with __debugInfo() crashes Xdebug due to a stack + overflow + - Fixed issue #1148: Can't disable max_nesting_function + - Fixed issue #1151: Crash when another extension calls call_user_function() + during RINIT + + - Fixed crash with code coverage (Antony Dovgal) + - Fixed usage of virtual_file_ex and STR_FREE (Remi Collet) + - Reset overloaded opcodes at the end of each request (Eran Ifrah) + += Improvements: + + - Fixed issue #686: Not possible to inspect SplObjectStorage instances with + Xdebug + - Fixed issue #864: No attributes are shown if an object extends + ArrayIterator + - Fixed issue #996: Can't evaluate property of class that extends ArrayObject + - Fixed issue #1134: Allow introspection of ArrayObject implementation's + internal storage + - Get rid of setlocale hack, by using %F instead of %f (and speed up tracing + by 15-20%) + + + + 2015-03-22 + + + 2.3.2 + 2.3.2 + + + stable + stable + + BSD style + +Sun, Mar 22, 2015 - xdebug 2.3.2 + += Fixed bugs: + + - Fixed issue #1117: Path/branch coverage sometimes crashes + - Fixed issue #1121: Segfaults with path/branch coverage + + + + 2015-02-24 + + + 2.3.1 + 2.3.1 + + + stable + stable + + BSD style + +Tue, Feb 24, 2015 - xdebug 2.3.1 + += Fixed bugs: + + - Fixed issue #1112: Setting an invalid xdebug.trace_format causes Xdebug to + crash + - Fixed issue #1113: xdebug.*_trigger do no longer work, due to NULL not + being an empty string + + + + 2015-02-22 + + + 2.3.0 + 2.3.0 + + + stable + stable + + BSD style + +Sun, Feb 22, 2015 - xdebug 2.3.0 + += Fixed bugs: + + - Fixed bug #932: Added an error message in case the remote debug log + couldn't be opened + - Fixed bug #982: Incorrect file paths in exception stack trace + - Fixed bug #1094: Segmentation fault when attempting to use branch/path + coverage + - Fixed bug #1101: Debugger is not triggered on xdebug_break() in JIT mode + - Fixed bug #1102: Stop Xdebug from crashing when debugging PHP Code with + "php -r". + - Fixed bug #1103: XDEBUG_SESSION_STOP_NO_EXEC only stops first script + executed with auto_prepend|append_files + - Fixed bug #1104: One character non-public properties cause issues with + debugging + - Fixed bug #1105: Setting properties without specifying a type only works in + topmost frame (Dominik del Bondio) + - Fixed bug #1095: Crash when using a non-associate array key in GLOBALS + - Fixed bug #1111: eval does not work when debugger is stopped in + xdebug_throw_exception_hook (Dominik del Bondio) + ++ Added features: + + - General + + - Implemented issue #304: File name and line number info for overloaded + var_dump() + - Implemented issue #310: Allow class vars and array keys with + xdebug_debug_zval() + - Implemented issue #722: Add stack trace limit setting. + - Implemented issue #1003: Add option to xdebug_print_function_stack() to + suppress filename and line number + - Implemented issue #1004: Ability to halt on warning/notice + - Implemented issue #1023: Add support for PHP 5.6 variadics + - Implemented issue #1024: Add support for PHP 5.6's ASSIGN_POW + + - Debugging + + - Implemented issue #406: Added support for remote debugging user-defined + constants + - Implemented issue #495: Added support for the wildcard exception name '*' + - Implemented issue #1066: Better error message for SELinux preventing + debugging connections + - Implemented issue #1084: Added support for extended classes to trigger + exception breakpoints + - Implemented issue #1084: Added exception code as extra element to + debugger XML + + - Tracing + + - Implemented issue #341: Added the time index and memory usage for + function returns in normal tracefiles + - Implemented issue #644: Shared secret for profiler_enable_trigger and + trace_enable_trigger with *_value option + - Implemented issue #971: Added the trace file option + "XDEBUG_TRACE_NAKED_FILENAME" to xdebug_start_trace() to prevent the + ".xt" extension from being added + - Implemented issue #1021: Added support for return values to computerized + trace files + - Implemented issue #1022: Added support for serialized variables as format + in trace files in the form of option "5" for "xdebug.collect_params" + + - Code coverage + + - Implemented issue #380: Added xdebug_code_coverage_started() + - Implemented issue #1034: Add collected path and branch information to + xdebug_get_code_coverage() output + + - Profiling + + - Implement issue #1054: Support for filename and function name compression + in cachegrind files + ++ Changes: + + - Implemented issue #863: Support xdebug.overload_var_dump through + ini_set() + - Implemented issue #973: Use case-insensitive filename comparison on all + systems (Galen Wright-Watson) + - Implemented issue #1015: Added the xdebug.force_display_errors and + xdebug.force_error_reporting php.ini-only settings to always override + PHP's settings for display_errors and error_reporting + - Implemented issue #1057: Removed trailing whitespace from example + xdebug.ini + - Implemented issue #1096: Improve performance improvement for handling + breakpoints by ignoring locales (Daniel Sloof) + - Implemented issue #1100: Raise default max_nesting_level to 256 + +- Removed features: + + - Support for PHP versions lower than PHP 5.4 have been dropped. + + + + 2015-01-21 + + + 2.2.7 + 2.2.7 + + + stable + stable + + BSD style + +Thu, Jan 22, 2014 - xdebug 2.2.7 + += Fixed bugs: + + - Fixed bug #1083: Segfault when requesting a variable for a context that did + not have them. + - Fixed bug #1087: zend_execute_script or zend_eval_string in RINIT segfaults. + - Fixed bug #1088: Xdebug won't show dead and not executed lines at the second + time. + - Fixed bug #1098: Xdebug doesn't make use of __debugInfo. + - Fixed segfaults with ZTS on PHP 5.6. + + + + 2014-11-14 + + + 2.2.6 + 2.2.6 + + + stable + stable + + BSD style + +Fri, Nov 14, 2014 - xdebug 2.2.6 + += Fixed bugs: + + - Fixed bug #1048: Can not get $GLOBAL variable by property_value on function + context. + - Fixed bug #1073 and #1075: Segmentation fault with internal functions + calling internal functions. + - Fixed bug #1085: Fixed the tracefile analyser as the format version had been + bumbed. + - Fixed memory leaks + + + + 2014-04-29 + + + 2.2.5 + 2.2.5 + + + stable + stable + + BSD style + +Tue, Apr 29, 2014 - xdebug 2.2.5 + += Fixed bugs: + + - Fixed bug #1040: Fixed uninitialized sa value. + - Fixed building on hurd-i386. + + + + 2014-02-28 + + + 2.2.4 + 2.2.4 + + + stable + stable + + BSD style + +Fri, Feb 28, 2014 - xdebug 2.2.4 + += Fixed bugs: + + - Fixed bug #785: Profiler does not handle closures and call_user_func_array well. + - Fixed bug #963: Xdebug waits too long for response from remote client + - Fixed bug #976: XDebug crashes if current varibles scope contains COM object. + - Fixed bug #978: Inspection of array with negative keys fails + - Fixed bug #979: property_value -m 0 should mean all bytes, not 0 bytes + - Fixed bug #987: Hidden property names not shown. + + + + 2013-05-22 + + + 2.2.3 + 2.2.3 + + + stable + stable + + BSD style + +Tue, May 21, 2013 - xdebug 2.2.3 + ++ Added features: + + - Support for PHP 5.5. + += Fixed bugs: + + - Fixed bug #923: Xdebug + Netbeans + ext/MongoDB crash on MongoCursor instance + - Fixed bug #929: Directory name management in xdebug.profiler_output_dir + - Fixed bug #931: xdebug_str_add does not check for NULL str before calling strlen on it + - Fixed bug #935: Document the return value from xdebug_get_code_coverage() + - Fixed bug #947: Newlines converted when html_errors = 0 + + + + 2013-03-23 + + + 2.2.2 + 2.2.2 + + + stable + stable + + BSD style + +Sat, Mar 23, 2013 - xdebug 2.2.2 + ++ Added features: + + - Support for PHP 5.5. + += Fixed bugs: + + - Fixed bug #598: Use HTTP_X_FORWARDED_FOR to determine remote debugger. + - Fixed bug #625: xdebug_get_headers() -> Headers are reset unexpectedly. + - Fixed bug #811: PHP Documentation Link. + - Fixed bug #818: Require a php script in the PHP_RINIT causes Xdebug to crash. + - Fixed bug #903: xdebug_get_headers() returns replaced headers. + - Fixed bug #905: Support PHP 5.5 and generators. + - Fixed bug #920: AM_CONFIG_HEADER is depreciated. + + + + + 2.2.1 + 2.2.1 + + + stable + stable + + 2012-07-14 + BSD style + += Fixed bugs: + + - Fixed bug #843: Text output depends on php locale. + - Fixed bug #838/#839/#840: Debugging static properties crashes Xdebug. + - Fixed bug #821: Variable assignments (beginning with =>) should be + indented one more scope. + - Fixed bug #811: PHP Documentation Link. + - Fixed bug #800: var_dump(get_class(new foo\bar')) add an extra "\" in + class name. + + + + + 2.2.0 + 2.2.0 + + + stable + stable + + 2012-05-08 + BSD style + +Tue, May 08, 2012 - xdebug 2.2.0 + ++ Added features: + + - Support for PHP 5.4. + + - Added ANSI colour output for the shell. (Including patches by Michael + Maclean) + - Added var_dump() overloading on the command line (issue #457). + + - Added better support for closures in stack and function traces. + - Added the size of arrays to the overloaded variable output, so that you + know how many elements there are. + - Added support for X-HTTP-FORWARDED-FOR before falling back to REMOTE_ADDR + (issue #660). (Patch by Hannes Magnusson) + - Added the method call type to xdebug_get_function_stack() (issue #695). + - Added extra information to error printouts to tell that the error + suppression operator has been ignored due to xdebug.scream. + - Added a error-specific CSS class to stack traces. + ++ New settings: + + - xdebug.cli_color for colouring output on the command line (Unix only). + - Added xdebug.trace_enable_trigger to triger function traces through a + GET/POST/COOKIE parameter (issue #517). (Patch by Patrick Allaert) + - Added support for the 'U' format specifier for function trace and + profiler filenames. + ++ Changes: + + - Improved performance by lazy-initializing data structures. + - Improved code coverage performance. (Including some patches by Taavi + Burns) + - Improved compatibility with KCacheGrind. + - Improved logging of remote debugging connections, by added connection + success/failure logging to the xdebug.remote_log functionality. + += Fixed bugs: + + - Fixed bug #827: Enabling Xdebug causes phpt tests to fail because of + var_dump() formatting issues. + - Fixed bug #823: Single quotes are escaped in var_dumped string output. + - Fixed issue #819: Xdebug 2.2.0RC2 can't stand on a breakpoint more than 30 seconds. + - Fixed bug #801: Segfault with streamwrapper and unclosed $fp on + destruction. + - Fixed issue #797: Xdebug crashes when fetching static properties. + - Fixed bug #794: Allow coloured output on Windows. + - Fixed bug #784: Unlimited feature for var_display_max_data and + var_display_max_depth is undocumented. + - Fixed bug #774: Apache crashes on header() calls. + - Fixed bug #764: Tailored Installation instructions do not work. + - Fixed bug #758: php_value xdebug.idekey is ignored in .htaccess files + - Fixed bug #728: Profiler reports __call() invocations confusingly/wrongly. + - Fixed bug #687: Xdebug does not show dynamically defined variable. + - Fixed bug #662: idekey is set to running user. + - Fixed bug #627: Added the realpath check. + + + + + 2.2.0RC2 + 2.2.0RC2 + + + stable + stable + + 2012-04-22 + BSD style + +Tue, Apr 22, 2012 - xdebug 2.2.0rc2 + += Fixed bugs: + + - Fixed bug #801: Segfault with streamwrapper and unclosed $fp on + destruction. + - Fixed bug #794: Allow coloured output on Windows. + - Fixed bug #784: Unlimited feature for var_display_max_data and + var_display_max_depth is undocumented. + - Fixed bug #774: Apache crashes on header() calls. + - Fixed bug #764: Tailored Installation instructions do not work. + - Fixed bug #758: php_value xdebug.idekey is ignored in .htaccess files + - Fixed bug #662: idekey is set to running user. + + + + + 2.2.0RC1 + 2.2.0RC1 + + + stable + stable + + 2012-03-12 + BSD style + +Tue, Mar 13, 2012 - xdebug 2.2.0rc1 + ++ Added features: + + - Support for PHP 5.4. + + - Added ANSI colour output for the shell. (Including patches by Michael + Maclean) + - Added var_dump() overloading on the command line (issue #457). + + - Added better support for closures in stack and function traces. + - Added the size of arrays to the overloaded variable output, so that you + know how many elements there are. + - Added support for X-HTTP-FORWARDED-FOR before falling back to REMOTE_ADDR + (issue #660). (Patch by Hannes Magnusson) + - Added the method call type to xdebug_get_function_stack() (issue #695). + - Added extra information to error printouts to tell that the error + suppression operator has been ignored due to xdebug.scream. + - Added a error-specific CSS class to stack traces. + + ++ New settings: + + - xdebug.cli_color for colouring output on the command line (Unix only). + - Added xdebug.trace_enable_trigger to triger function traces through a + GET/POST/COOKIE parameter (issue #517). (Patch by Patrick Allaert) + - Added support for the 'U' format specifier for function trace and + profiler filenames. + ++ Changes: + + - Improved performance by lazy-initializing data structures. + - Improved code coverage performance. (Including some patches by Taavi + Burns) + - Improved compatibility with KCacheGrind. + - Improved logging of remote debugging connections, by added connection + success/failure logging to the xdebug.remote_log functionality. + += Fixed bugs: + + - No additional bug fixes besides the ones from the 2.1 branch up til + Xdebug 2.1.4. + + + + + 2.1.4 + 2.1.4 + + + stable + stable + + 2012-03-12 + BSD style + += Fixed bugs: + + - Fixed bug #788: Collect errors eats fatal errors. + - Fixed bug #787: Segmentation Fault with PHP header_remove(). + - Fixed bug #778: Xdebug session in Eclipse crash whenever it run into + simplexml_load_string call. + - Fixed bug #756: Added support for ZEND_*_*_OBJ and self::*. + - Fixed bug #747: Still problem with error message and soap client / soap + server. + - Fixed bug #744: new lines in a PHP file from Windows are displayed with + an extra white line with var_dump(). + - Fixed an issue with debugging and the eval command. + - Fixed compilation with ZTS on PHP < 5.3 + + + + + 2.1.3 + 2.1.3 + + + stable + stable + + 2012-01-25 + BSD style + += Fixed bugs: + + - Fixed bug #725: EG(current_execute_data) is not checked in xdebug.c, + xdebug_statement_call. + - Fixed bug #723: xdebug is stricter than PHP regarding Exception property + types. + - Fixed bug #714: Cachegrind files have huge (wrong) numbers in some lines. + - Fixed bug #709: Xdebug doesn't understand E_USER_DEPRECATED. + - Fixed bug #698: Allow xdebug.remote_connect_back to be set in .htaccess. + - Fixed bug #690: Function traces are not appended to file with + xdebug_start_trace() and xdebug.trace_options=1. + - Fixed bug #623: Static properties of a class can be evaluated only with + difficulty. + - Fixed bug #614/#619: Viewing private variables in base classes through + the debugger. + - Fixed bug #609: Xdebug and SOAP extension's error handlers conflict. + - Fixed bug #606/#678/#688/#689/#704: crash after using eval on an + unparsable, or un-executable statement. + - Fixed bug #305: xdebug exception handler doesn't properly handle special + chars. + ++ Changes: + + - Changed xdebug_break() to hint to the statement execution trap instead of + breaking forcefully adding an extra stackframe. + - Prevent Xdebug 2.1.x to build with PHP 5.4. + + + + + 2.1.2 + 2.1.2 + + + stable + stable + + 2011-07-28 + BSD style + += Fixed bugs: + + - Fixed bug #622: Working with eval() code is inconvenient and difficult. + - Fixed bug #684: xdebug_var_dump - IE does not support &. + - Fixed bug #693: Cachegrind files not written when filename is very long. + - Fixed bug #697: Incorrect code coverage of function arguments when using + XDEBUG_CC_UNUSED. + - Fixed bug #699: Xdebug gets the filename wrong for the countable + interface. + - Fixed bug #703 by adding another opcode to the list that needs to be + overridden. + + + + + 2.1.2 + 2.1.2 + + + stable + stable + + 2011-07-28 + BSD style + += Fixed bugs: + + - Fixed bug #622: Working with eval() code is inconvenient and difficult. + - Fixed bug #684: xdebug_var_dump - IE does not support &. + - Fixed bug #693: Cachegrind files not written when filename is very long. + - Fixed bug #697: Incorrect code coverage of function arguments when using + XDEBUG_CC_UNUSED. + - Fixed bug #699: Xdebug gets the filename wrong for the countable + interface. + - Fixed bug #703 by adding another opcode to the list that needs to be + overridden. + + + + + 2.1.1 + 2.1.1 + + + stable + stable + + 2011-03-28 + BSD style + +Mon, Mar 28, 2011 - xdebug 2.1.1 + += Fixed bugs: + + - Fixed ZTS compilation. + + + + + 2.1.1RC1 + 2.1.1RC1 + + + beta + beta + + 2011-03-22 + BSD style + +Tue, Mar 22, 2011 - xdebug 2.1.1rc1 + += Fixed bugs: + + = Debugger + - Fixed bug #518: Removed CLASSNAME pseudo-property optional. + - Fixed bug #592: Xdebug crashes with run after detach. + - Fixed bug #596: Call breakpoint never works with instance methods, only + static methods. + - Fixed JIT mode in the debugger so that it works for xdebug_break() too. + + = Profiler + - Fixed bug #631: Summary not written when script ended with "exit()". + - Fixed bug #639: Xdebug profiling: output not correct - missing 'cfl='. + - Fixed bug #642: Fixed line numbers for offsetGet, offsetSet, + __get/__set/__isset/__unset and __call in profile files and stack + traces/function traces. + - Fixed bug #643: Profiler gets line numbers wrong. + - Fixed bug #653: XDebug profiler crashes with %H in file name and non + standard port. + + = Others + - Fixed bug #651: Incorrect code coverage after empty() in conditional. + - Fixed bug #654: Xdebug hides error message in CLI. + - Fixed bug #665: Xdebug does not respect display_errors=stderr. + Patch by Ben Spencer <dangerous.ben@gmail.com> + - Fixed bug #670: Xdebug crashes with broken "break x" code. + + + + + 2.1.0 + 2.1.0 + + + stable + stable + + 2010-06-29 + BSD style + +Tue, Jun 29, 2010 - xdebug 2.1.0 + += Fixed bugs: + - Fixed bug #562: Incorrect coverage information for closure function + headers. + - Fixed bug #566: Xdebug crashes when using conditional breakpoints. + - Fixed bug #567: xdebug_debug_zval and xdebug_debug_zval_stdout don't work + with PHP 5.3. (Patch by Endo Hiroaki). + - Fixed bug #570: undefined symbol: zend_memrchr. + + + + + 2.1.0RC1 + 2.1.0RC1 + + + beta + beta + + 2010-02-27 + BSD style + +Thu, Apr 06, 2010 - xdebug 2.1.0rc1 + += Fixed bugs: + - Fixed bug #494: Private attributes of parent class unavailable when + inheriting. + - Fixed bug #400: Xdebug shows errors, even when PHP is request startup + mode. + - Fixed bug #421: xdebug sends back invalid characters in xml sometimes. + - Fixed bug #475: Property names with null chars are not sent fully to the + client. + - Fixed bug #480: Issues with the reserved resource in multi threaded + environments (Patch by Francis.Grolemund@netapp.com). + - Fixed bug #558: PHP segfaults when running a nested eval. + + + + + 2.1.0beta3 + 2.1.0beta3 + + + beta + beta + + 2010-02-27 + BSD style + +Sat, Feb 27, 2010 - xdebug 2.1.0beta3 + += Fixed bugs: + - Fixed memory corruption issues. + - Fixed a threading related issue for code-coverage. + - Fixed bug #532: XDebug breaks header() function. + - DBGP: Prevent Xdebug from returning properties when a too high page number + has been requested. + + + + + 2.1.0beta2 + 2.1.0beta2 + + + beta + beta + + 2010-02-03 + BSD style + +Wed, Feb 03, 2010 - xdebug 2.1.0beta2 + += Fixed bugs: + - Fixed memory leak in breakpoint handling. + - Fixed bug #528: Core dump generated with remote_connect_back option set + and CLI usage. + - Fixed bug #515: declare(ticks) statement confuses code coverage. + - Fixed bug #512: DBGP: breakpoint_get doesn't return conditions in its + response. + - Possible fix for bug #507/#517: Crashes because of uninitalised header + globals. + - Fixed bug #501: Xdebug's variable tracing misses POST_INC and variants. + + + + + 2.1.0beta1 + 2.1.0beta1 + + + beta + beta + + 2010-01-03 + BSD style + +Sun, Jan 03, 2010 - xdebug 2.1.0beta1 + ++ Added features: + - Added error display collection and suppressions. + - Added the recording of headers being set in scripts. + - Added variable assignment tracing. + - Added the ability to turn of the default overriding of var_dump(). + - Added "Scream" support, which disables the @ operator. + - Added a trace-file analysing script. + - Added support for debugging into phars. + - Added a default xdebug.ini. (Patch by Martin Schuhfu + <martins@spot-media.de>) + - Added function parameters in computerized function traces. + - PHP 5.3 compatibility. + - Improved code coverage accuracy. + + + New functions: + - xdebug_get_formatted_function_stack(), which returns a formatted function + stack instead of displaying it. + - xdebug_get_headers(), which returns all headers that have been set in a + script, both explicitly with things like header(), but also implicitly + for things like setcookie(). + - xdebug_start_error_collection(), xdebug_stop_error_collection() and + xdebug_get_collected_errors(), which allow you to collect all notices, + warnings and error messages that Xdebug generates from PHP's + error_reporting functionality so that you can output them at a later + point in your script by hand. + + + New settings: + - xdebug.collect_assignments, which enables the emitting of variable + assignments in function traces. + - xdebug.file_line_format, to generate a link with a specific format for + every filename that Xdebug outputs. + - xdebug.overload_var_dump, which allows you to turn off Xdebug's version + of var_dump(). + - xdebug.remote_cookie_expire_time, that controls the length of a + remote debugging session. (Patch by Rick Pannen <pannen@gmail.com>) + - xdebug.scream, which makes the @ operator to be ignored. + ++ Changes: + - Added return values for xdebug_start_code_coverage() and + xdebug_stop_code_coverage() to indicate whether the action was + successful. xdebug_start_code_coverage() will return TRUE if the call + enabled code coverage, and FALSE if it was already enabled. + xdebug_stop_code_coverage() will return FALSE when code coverage wasn't + started yet and TRUE if it was turned on. + - Added an optional argument to xdebug_print_function_stack() to display + your own message. (Patch by Mikko Koppanen). + - All HTML output as generated by Xdebug now has a HTML "class" attribute + for easy CSS formatting. + +- Removed features: + - Support for PHP versions lower than PHP 5.1 have been dropped. + - The PHP3 and GDB debugger engines have been removed. + += Fixed bugs: + - Fixed support for showing $this in remote debugging sessions. + - Fixed bug in formatting the display of "Variables in the local scope". + - Possible fix for a threading issue where the headers gathering function + would create stack overflows. + - Possible fix for #324: xdebug_dump_superglobals() only dumps superglobals + that were accessed before, and #478: XDebug 2.0.x can't use %R in + xdebug.profiler_output_name if register_long_arrays is off. + + - Fixed bug #505: %s in xdebug.trace_output_name breaks functions traces. + - Fixed bug #494: Private attributes of parent class unavailable when + inheriting. + - Fixed bug #486: feature_get -n breakpoint_types returns out of date list. + - Fixed bug #476: Xdebug doesn't support PHP 5.3's exception chaining. + - Fixed bug #472: Dead Code Analysis for code coverage messed up after goto. + - Fixed bug #470: Catch blocks marked as dead code unless executed. + - Fixed bug #469: context_get for function variables always appear as + "uninitialized". + - Fixed bug #468: Property_get on $GLOBALS works only at top-level, by + adding GLOBALS to the super globals context. + - Fixed bug #453: Memory leaks. + - Fixed bug #445: error_prepend_string and error_append_string are ignored + by xdebug_error_cb. (Patch by Kent Davidson <kent@marketruler.com>) + - Fixed bug #442: configure: error: "you have strange libedit". + - Fixed bug #439: Xdebug crash in xdebug_header_handler. + - Fixed bug #423: Conflicts with funcall. + - Fixed bug #419: Make use of P_tmpdir if defined instead of hard coded + '/tmp'. + - Fixed bug #417: Response of context_get may lack page and pagesize + attributes. + - Fixed bug #411: Class/function breakpoint setting does not follow the + specs. + - Fixed bug #393: eval returns array data at the previous page request. + - Fixed bug #391: Xdebug doesn't stop executing script on catchable fatal + errors. + - Fixed bug #389: Destructors called on fatal error. + - Fixed bug #368: Xdebug's debugger bails out on a parse error with the + eval command. + - Fixed bug #356: Temporary breakpoints persist. + - Fixed bug #355: Function numbers in trace files weren't unique. + - Fixed bug #340: Segfault while throwing an Exception. + - Fixed bug #328: Private properties are incorrectly enumerated in case of + extended classes. + - Fixed bug #249: Xdebug's error handler messes up with the SOAP + extension's error handler. + ++ DBGP: + - Fixed cases where private properties where shown for objects, but not + accessible. + - Added a patch by Lucas Nealan (lucas@php.net) and Brian Shire + (shire@php.net) of Facebook to allow connections to the initiating + request's IP address for remote debugging. + - Added the -p argument to the eval command as well, pending inclusion into + DBGP. + - Added the retrieval of a file's execution lines. I added a new + un-official method called xcmd_get_executable_lines which requires the + stack depth as argument (-d). You can only fetch this information for + stack frames as it needs an available op-array which is only available + when a function is executed. + - Added a fake "CLASSNAME" property to objects that are returned in debug + requests to facilitate deficiencies in IDEs that fail to show the "classname" + XML attribute. + + + + + 2.0.5 + 2.0.5 + + + stable + stable + + 2009-07-03 + BSD style + +Fri, Jul 03, 2009 - xdebug 2.0.5 + += Fixed bugs: + - Fixed bug #425: memory leak (around 40MB for each request) when using + xdebug_start_trace. + - Fixed bug #422: Segfaults when using code coverage with a parse error in + the script. + - Fixed bug #418: compilation breaks with CodeWarrior for NetWare. + - Fixed bug #403: 'call' and 'return' breakpoints triggers both on call and + return for class method breakpoints. + - Fixed TSRM issues for PHP 5.2 and PHP 5.3. (Original patch by Elizabeth + M. Smith). + - Fixed odd crash bugs, due to GCC 4 sensitivity. + + + + + 2.0.4 + 2.0.4 + + + stable + stable + + 2008-12-30 + BSD style + +Tue, Dec 30, 2008 - xdebug 2.0.4 + += Fixed bugs: + - Fixed for strange jump positions in path analysis. + - Fixed issues with code coverage crashing on parse errors. + - Fixed code code coverage by overriding more opcodes. + - Fixed issues with Xdebug stalling/crashing when detaching from remote + debugging. + - Fixed crash on Vista where memory was freed with routines from a different + standard-C library than it was allocated with. (Patch by Eric Promislow + <ericp@activestate.com>). + - Link against the correct CRT library. (Patch by Eric Promislow + <ericp@activestate.com>). + - Sort the symbol elements according to name. (Patch by Eric Promislow + <ericp@activestate.com>). + - Fixed support for mapped-drive UNC paths for Windows. (Patch by Eric + Promislow <ericp@activestate.com>). + - Fixed a segfault in interactive mode while including a file. + - Fixed a crash in super global dumping in case somebody was strange enough + to reassign them to a value type other than an Array. + - Simplify version checking for libtool. (Patch by PGNet + <pgnet.trash@gmail.com>). + - Fixed display of unused returned variables from functions in PHP 5.3. + - Include config.w32 in the packages as well. + - Fixed .dsp for building with PHP 4. + ++ Added features: + - Support debugging into phars. + - Basic PHP 5.3 support. + + + + + 2.0.3 + 2.0.3 + + + stable + stable + + 2008-04-09 + BSD style + +Wed, Apr 09, 2008 - xdebug 2.0.3 + += Fixed bugs: + - Fixed bug #338: Crash with: xdebug.remote_handler=req. + - Fixed bug #334: Code Coverage Regressions. + - Fixed abstract method detection for PHP 5.3. + - Fixed code coverage dead-code detection. + - Ignore ZEND_ADD_INTERFACE, which is on a different line in PHP >= 5.3 for + some weird reason. + ++ Changes: + - Added a CSS-class for xdebug's var_dump(). + - Added support for the new E_DEPRECATED. + + + + + 2.0.2 + 2.0.2 + + + stable + stable + + 2007-11-11 + BSD style + +Sun, Nov 11, 2007 - xdebug 2.0.2 + += Fixed bugs: + - Fixed bug #325: DBGP: "detach" stops further sessions being established + from Apache. + - Fixed bug #321: Code coverage crashes on empty PHP files. + - Fixed bug #318: Segmentation Fault in code coverage analysis. + - Fixed bug #315: Xdebug crashes when including a file that doesn't exist. + - Fixed bug #314: PHP CLI Error Logging thwarted when XDebug Loaded. + - Fixed bug #300: Direction of var_dump(). + - Always set the transaction_id and command. (Related to bug #313). + + + + + 2.0.1 + 2.0.1 + + + stable + stable + + 2007-10-29 + BSD style + +Sat, Oct 20, 2007 - xdebug 2.0.1 + ++ Changes: + - Improved code coverage performance dramatically. + - PHP 5.3 compatibility (no namespaces yet though). + += Fixed bugs: + - Fixed bug #301: Loading would cause SIGBUS on Solaris 10 SPARC. (Patch by + Sean Chalmers) + - Fixed bug #300: Xdebug does not force LTR rendering for its tables. + - Fixed bug #299: Computerized traces don't have a newline for return + entries if memory limit is not enabled. + - Fixed bug #298: xdebug_var_dump() doesn't handle entity replacements + correctly concerning string length. + - Fixed a memory free error related to remote debugging conditions. + (Related to bug #297). + + + + + 2.0.0 + 2.0.0 + + + stable + stable + + 2007-07-18 + BSD style + +Wed, Jul 18, 2007 - xdebug 2.0.0 + ++ Changes: + - Put back the disabling of stack traces - apperently people were relying + on this. This brings back xdebug_enable(), xdebug_disable() and + xdebug_is_enabled(). + - xdebug.collect_params is no longer a boolean setting. Although it worked + fine, phpinfo() showed only just On or Off here. + - Fixed the Xdebug version of raw_url_encode to not encode : and \. This is + not necessary according to the RFCs and it makes debug breakpoints work + on Windows. + += Fixed bugs: + - Fixed bug #291: Tests that use SPL do not skip when SPL is not available. + - Fixed bug #290: Function calls leak memory. + - Fixed bug #289: Xdebug terminates connection when eval() is run in the + init stage. + - Fixed bug #284: Step_over on breakpointed line made Xdebug break twice. + - Fixed bug #283: Xdebug always returns $this with the value of last stack + frame. + - Fixed bug #282: %s is not usable for xdebug.profiler_output_name on + Windows in all stack frames. + - Fixed bug #280: var_dump() doesn't display key of array as expected. + - Fixed bug #278: Code Coverage Issue. + - Fixed bug #273: Remote debugging: context_get does not return context id. + - Fixed bug #270: Debugger aborts when PHP's eval() is encountered. + - Fixed bug #265: XDebug breaks error_get_last() . + - Fixed bug #261: Code coverage issues by overloading zend_assign_dim. + ++ DBGP: + - Added support for "breakpoint_languages". + + + + + 2.0.0RC4 + 2.0.0RC4 + + + beta + beta + + 2007-05-17 + BSD style + +Wed, May 17, 2007 - xdebug 2.0.0rc4 ++ Changes: + - Use microseconds instead of a tenths of microseconds to avoid confusion in + profile information. + - Changed xdebug.profiler_output_name and xdebug.trace_output_name to use + modifier tags: + %c = crc32 of the current working directory + %p = pid + %r = random number + %s = script name + %t = timestamp (seconds) + %u = timestamp (microseconds) + %H = $_SERVER['HTTP_HOST'] + %R = $_SERVER['REQUEST_URI'] + %S = session_id (from $_COOKIE if set) + %% = literal % + += Fixed bugs: + - Fixed bug #255: Call Stack Table doesn't show Location on Windows. + - Fixed bug #251: Using the source command with an invalid filename returns + unexpected result. + - Fixed bug #243: show_exception_trace="0" ignored. + - Fixed bug #241: Crash in xdebug_get_function_stack(). + - Fixed bug #240: Crash with xdebug.remote_log on Windows. + - Fixed a segfault in rendering stack traces to error logs. + - Fixed a bug that prevented variable names from being recorded for remote + debug session while xdebug.collect_vars was turned off. + - Fixed xdebug_dump_superglobals() in case no super globals were + configured. + +- Removed functions: + - Removed support for Memory profiling as that didn't work properly. + - Get rid of xdebug.default_enable setting and associated functions: + xdebug_disable() and xdebug_enable(). + ++ Added features: + - Implemented support for four different xdebug.collect_params settings for + stack traces and function traces. + - Allow to trigger profiling by the XDEBUG_PROFILE cookie. + ++ DBGP: + - Correctly add namespace definitions to XML. + - Added the xdebug namespace that adds extra information to breakpoints if + available. + - Stopped the use of >error> elements for exception breakpoints, as that + violates the protocol. + + + + + 2.0.0RC3 + 2.0.0RC3 + + + beta + beta + + 2007-01-31 + BSD style + +Wed, Jan 31, 2007 - xdebug 2.0.0rc3 ++ Changes: + - Removed the bogus "xdebug.allowed_clients" setting - it was not + implemented. + - Optimized used variable collection by switching to a linked list instead + of a hash. This is about 30% faster, but it needed a quick conversion to + hash in the case the information had to be shown to remove duplicate + variable names. + += Fixed bugs: + - Fixed bug #232: PHP log_errors functionality lost after enabling xdebug + error handler when CLI is used. + - Fixed problems with opening files - the filename could cause double free + issues. + - Fixed memory tracking as memory_limit is always enabled in PHP 5.2.1 and + later. + - Fixed a segfault that occurred when creating printable stack traces and + collect_params was turned off. + + + + + 2.0.0RC2 + 2.0.0RC2 + + + beta + beta + + 2006-12-24 + BSD style + +Sun, Dec 24, 2006 - xdebug 2.0.0rc2 ++ Added new features: + - Implemented the "xdebug.var_display_max_children" setting. The default is + set to 128 children. + - Added types to fancy var dumping function. + - Implemented FR #210: Add a way to stop the debug session without having + to execute a script. The GET/POST parameter "XDEBUG_SESSION_STOP_NO_EXEC" + works in the same way as XDEBUG_SESSION_STOP, except that the script will + not be executed. + - DBGP: Allow postmortem analysis. + - DBGP: Added the non-standard function xcmd_profiler_name_get. + ++ Changes: + - Fixed the issue where xdebug_get_declared_vars() did not know about + variables there are in the declared function header, but were not used in + the code. Due to this change expected arguments that were not send to a + function will now show up as ??? in stack and function traces in PHP 5.1 + and PHP 5.2. + - Allow xdebug.var_display_max_data and xdebug.var_display_max_depth + settings of -1 which will unlimit those settings. + - DBGP: Sort super globals in Globals overview. + - DBGP: Fixed a bug where error messages where not added upon errors in the + protocol. + - DBGP: Change context 1 from globals (superglobals + vars in bottom most + stack frame) to just superglobals. + += Fixed bugs: + - Fixed linking error on AIX by adding libm. + - Fixed dead code analysis for THROW. + - Fixed oparray prefill caching for code coverage. + - Fixed the xdebug.remote_log feature work. + - DBGP: Fixed a bug where $this did not appear in the local scoped context. + - DBGP: Reimplemented property_set to use the same symbol fetching function + as property_get. We now only use eval in case no type (-t) argument was + given. + - DBGP: Fixed some issues with finding out the classname, which is + important for fetching private properties. + - DBGP: Fixed usage of uninitialized memory that prevented looking up + numerical array keys while fetching array elements not work properly. + - Fixed bug #228: Binary safety for stream output and property fetches. + - Fixed bug #227: The SESSION super global does not show up in the Globals + scope. + - Fixed bug #225: xdebug dumps core when protocol is GDB. + - Fixed bug #224: Compile failure on Solaris. + - Fixed bug #219: Memory usage delta in traces don't work on PHP 5.2.0. + - Fixed bug #215: Cannot retrieve nested arrays when the array key is a + numeric index. + - Fixed bug #214: The depth level of arrays was incorrectly checked so it + would show the first page of a level too deep as well. + - Fixed bug #213: Dead code analysis doesn't take catches for throws into + account. + - Fixed bug #211: When starting a new session with a different idekey, the + cookie is not updated. + - Fixed bug #209: Additional remote debugging session started when + triggering shutdown function. + - Fixed bug #208: Socket connection attempted when XDEBUG_SESSION_STOP. + - Fixed PECL bug #8989: Compile error with PHP 5 and GCC 2.95. + + + + + 2.0.0rc1 + 2.0.0rc1 + + + beta + beta + + 2006-10-08 + BSD style + ++ Added new features: + - Implemented FR #70: Provide optional depth on xdebug_call_* functions. + - Partially implemented FR #50: Resource limiting for variable display. By + default only two levels of nested variables and max string lengths of 512 + are shown. This can be changed by setting the ini settings + xdebug.var_display_max_depth and xdebug.var_display_max_data. + - Implemented breakpoints for different types of PHP errors. You can now + set an 'exception' breakpoint on "Fatal error", "Warning", "Notice" etc. + This is related to bug #187. + - Added the xdebug_print_function_trace() function to display a stack trace on + demand. + - Reintroduce HTML tracing by adding a new tracing option "XDEBUG_TRACE_HTML" + (4). + - Made xdebug_stop_trace() return the trace file name, so that the + following works: <?php echo file_get_contents( xdebug_stop_trace() ); ?> + - Added the xdebug.collect_vars setting to tell Xdebug to collect + information about which variables are used in a scope. Now you don't need + to show variables with xdebug.show_local_vars anymore for + xdebug_get_declared_vars() to work. + - Make the filename parameter to the xdebug_start_trace() function + optional. If left empty it will use the same algorithm to pick a filename + as when you are using the xdebug.auto_trace setting. + ++ Changes: + - Implemented dead code analysis during code coverage for: + * abstract methods. + * dead code after return, throw and exit. + * implicit returns when a normal return is present. + - Improved readability of stack traces. + - Use PG(html_errors) instead of checking whether we run with CLI when + deciding when to use HTML messages or plain text messages. + += Fixed bugs: + - Fixed bug #203: PHP errors with HTML content processed incorrectly. This + patch backs out the change that was made to fix bug #182. + - Fixed bug #198: Segfault when trying to use a non-existing debug handler. + - Fixed bug #197: Race condition fixes created too many files. + - Fixed bug #196: Profile timing on Windows does not work. + - Fixed bug #195: CLI Error after debugging session. + - Fixed bug #193: Compile problems with PHP 5.2. + - Fixed bug #191: File/line breakpoints are case-sensitive on Windows. + - Fixed bug #181: Xdebug doesn't handle uncaught exception output + correctly. + - Fixed bug #173: Coverage produces wrong coverage. + - Fixed a typo that prevented the XDEBUG_CONFIG option "profiler_enable" + from working. + + + + + 2.0.0beta6 + 2.0.0beta6 + + + beta + beta + + 2006-06-30 + BSD style + ++ Added new features: + - Implemented FR #137: feature_get for general commands doesn't have a text field. + - Implemented FR #131: XDebug needs to implement paged child object requests. + - Implemented FR #124: Add backtrace dumping information when exception thrown. + - Implemented FR #70: Add feature_get breakpoint_types. + - Added profiling aggregation functions (patch by Andrei Zmievski) + - Implemented the "timestamp" option for the xdebug.trace_output_name and + xdebug.profiler_output_name settings. + - Added the xdebug.remote_log setting that allows you to log debugger + communication to a log file for debugging. This can also be set through + the "remote_log" element in the XDEBUG_CONFIG environment variable. + - Added a "script" value to the profiler_output_name option. This will write + the profiler output to a filename that consists of the script's full path + (using underscores). ie: /var/www/index.php becomes + var_www_index_php_cachegrind.out. (Patch by Brian Shire). + - DBGp: Implemented support for hit conditions for breakpoints. + - DBGp: Added support for conditions for file/line breakpoints. + - DBGp: Added support for hit value checking to file/line breakpoints. + - DBGp: Added support for "exception" breakpoints. ++ Performance improvements: + - Added a cache that prevents the code coverage functionality from running a + "which code is executable check" on every function call, even if they + were executed multiple times. This should speed up code coverage a lot. + - Speedup Xdebug but only gathering information about variables in scopes when + either remote debugging is used, or show_local_vars is enabled. += Fixed bugs: + - Fixed bug #184: problem with control chars in code traces + - Fixed bug #183: property_get -n $this->somethingnonexistent crashes the + debugger. + - Fixed bug #182: Errors are not html escaped when being displayed. + - Fixed bug #180: collected includes not shown in trace files. (Patch by + Cristian Rodriguez) + - Fixed bug #178: $php_errormsg and Track errors unavailable. + - Fixed bug #177: debugclient fails to compile due to Bison. + - Fixed bug #176: Segfault using SplTempFileObject. + - Fixed bug #173: Xdebug segfaults using SPL ArrayIterator. + - Fixed bug #171: set_time_limit stack overflow on 2nd request. + - Fixed bug #168: Xdebug's DBGp crashes on an eval command where the + result is an array. + - Fixed bug #125: show_mem_delta does not calculate correct negative values on + 64bit machines. + - Fixed bug #121: property_get -n $r[2] returns the whole hash. + - Fixed bug #111: xdebug does not ignore set_time_limit() function during debug + session. + - Fixed bug #87: Warning about headers when "register_shutdown_function" used. + - Fixed PECL bug #6940 (XDebug ignores set_time_limit) + - Fixed Komodo bug 45484: no member data for objects in PHP debugger. + - Suppress NOP/EXT_NOP from being marked as executable code with Code + Coverage. + + + + + 2.0.0beta5 + 2.0.0beta5 + + + beta + beta + + 2005-12-31 + BSD style + ++ Added new features: + - Implemented FR #161: var_dump doesn't show lengths for strings. + - Implemented FR #158: Function calls from the {main} scope always have the + line number 0. + - Implemented FR #156: it's impossible to know the time taken by the last + func call in xdebug trace mode 0. + - Implemented FR #153: xdebug_get_declared_vars(). + += Fixed bugs: + - Fixed shutdown crash with ZTS on Win32 + - Fixed bad memory leak when a E_ERROR of exceeding memory_limit was + thrown. + - Fixed bug #154: GCC 4.0.2 optimizes too much out with -O2. + - Fixed bug #141: Remote context_get causes segfault. + + + + + 2.0.0beta4 + 2.0.0beta4 + + + beta + beta + + 2005-09-24 + BSD style + ++ Added new features: + - Added xdebug_debug_zval_stdout(). + - Added xdebug_get_profile_filename() function which returns the current + profiler dump file. + - Updated for latest 5.1 and 6.0 CVS versions of PHP. + - Added FR #148: Option to append to cachegrind files, instead of + overwriting. + - Implemented FR #114: Rename tests/*.php to tests/*.inc + +- Changed features: + - Allow "xdebug.default_enable" to be set everywhere. + += Fixed bugs: + - DBGP: Xdebug should return "array" with property get, which is defined + in the typemap to the common type "hash". + - Fixed bug #142: xdebug crashes with implicit destructor calls. + - Fixed bug #136: The "type" attribute is missing from stack_get returns. + - Fixed bug #133: PHP scripts exits with 0 on PHP error. + - Fixed bug #132: use of eval causes a segmentation fault. + + + + + 2.0.0beta3 + 2.0.0beta3 + + + beta + beta + + 2005-05-12 + BSD style + ++ Added new features: + - Added the possibility to trigger the profiler by setting + "xdebug.profiler_enable_trigger" to 1 and using XDEBUG_PROFILE as a get + parameter. + += Fixed bugs: + - Fixed a segfault for when an attribute value is NULL on XML string + generation. + - Fixed bug #118: Segfault with exception when remote debugging. + - Fixed bug #117: var_dump dows not work with "private". + - Fixed bug #109: DBGP's eval will abort the script when the eval statement + is invalid. + - Fixed bug #108: log_only still displays some text for errors in included + files. + - Fixed bug #107: Code Coverage only detects executable code in used + functions and classes. + - Fixed bug #103: crash when running the DBGp command 'eval' on a global + variable + - Fixed bug #95: Segfault when deinitializing Xdebug module. + (Patch by Maxim Poltarak <demiurg@gmail.com>) + + + + + 2.0.0beta2 + 2.0.0beta2 + + + beta + beta + + 2004-11-28 + BSD style + ++ Added new features: + - DBGP: Added error messages to returned errors (in most cases) + ++ Added new functions: + - xdebug_debug_zval() to debug zvals by printing its refcounts and is_ref + values. + += Changed features: + - xdebug_code_coverage_stop() will now clean up the code coverage array, + unless you specify FALSE as parameter. + - The proper Xdebug type is "hash" for associative arrays. + - Extended the code-coverage functionality by returning lines with + executable code on them, but where not executed with a count value of -1. + += Fixed bugs: + - DBGP: Make property_get and property_value finally work as they should, + including retrieving information from different depths then the most top + stack frame. + - DBGP: Fix eval'ed $varnames in property_get. + - DBGP: Support the -d option for property_get. + - Fixed the exit handler hook to use the new "5.1" way of handling it; + which fortunately also works with PHP 5.0. + - Fixed bug #102: Problems with configure for automake 1.8. + - Fixed bug #101: crash with set_exeception_handler() and uncatched exceptions. + - Fixed bug #99: unset variables return the name as a string with property_get. + - Fixed bug #98: 'longname' attribute not returned for uninitialized + property in context_get request. + - Fixed bug #94: xdebug_sprintf misbehaves with x86_64/glibc-2.3.3 + - Fixed bug #93: Crash in lookup_hostname on x86_64 + - Fixed bug #92: xdebug_disable() doesn't disable the exception handler. + - Fixed bug #68: Summary not written when script ended with "exit()". + + + + + 2.0.0beta1 + 2.0.0beta1 + + + beta + beta + + 2004-09-15 + BSD style + ++ Added new features: + - Added support for the new DBGp protocol for communicating with the debug + engine. + - A computerized trace format for easier parsing by external programs. + - The ability to set remote debugging features via the environment. This + allows an IDE to emulate CGI and still pass the configuration through to + the debugger. In CGI mode, PHP does not allow -d arguments. + - Reimplementation of the tracing code, you can now only trace to file; this greatly + enhances performance as no string representation of variables need to be + kept in memory any more. + - Re-implemented profiling support. Xdebug outputs information the same way + that cachegrind does so it is possible to use Kcachegrind as front-end. + - Xdebug emits warnings when it was not loaded as a Zend extension. + - Added showing private, protected and public to the fancy var_dump() + replacement function. + - Added the setting of the TCP_NODELAY socket option to stop delays in + transferring data to the remote debugger client. (Patch by Christof J. Reetz) + + DebugClient: Added setting for port to listen on and implemented running + the previous command when pressing just enter. + ++ Added new functions: + - xdebug_get_stack_depth() to return the current stack depth level. + - xdebug_get_tracefile_name() to retrieve the name of the tracefile. This + is useful in case auto trace is enabled and you want to clean the trace + file. + - xdebug_peak_memory_usage() which returns the peak memory + used in a script. (Only works when --enable-memory-limit was enabled) + ++ Added feature requests: + - FR #5: xdebug_break() function which interupts the script for the debug + engine. + - FR #30: Dump current scope information in stack traces on error. + - FR #88: Make the url parameter XDEBUG_SESSION_START optional. So it can + be disabled and the user does not need to add it. + ++ Added new php.ini settings: + - xdebug.auto_trace_file: to configure a trace file to write to as addition + to the xdebug.auto_trace setting which just turns on tracing. + - xdebug.collect_includes: separates collecting + names of include files from the xdebug.collect_params setting. + - xdebug.collect_return: showing return values in traces. + - xdebug.dump_global: with which you can turn off dumping of super globals + even in you have that configured. + - xdebug.extended_info: turns off the generation of extended opcodes that + are needed for stepping and breakpoints for the remote debugger. This is + useful incase you want to profile memory usage as the generation of this + extended info increases memory usage of oparrrays by about 33%. + - xdebug.profiler_output_dir: profiler output directory. + - xdebug.profiler_enable: enable the profiler. + - xdebug.show_local_vars: turn off the showing of local variables in the + top most stack frame on errors. + - xdebug.show_mem_delta: show differences between current and previous + memory usage on a function call level. + - xdebug.trace_options: to configure extra + options for trace dumping: + o XDEBUG_TRACE_APPEND option (1) + += Changed features: + - xdebug_start_trace() now returns the filename of the tracefile (.xt is + added to the requested name). + - Changed default debugging protocol to dbgp instead of gdb. + - Changed default debugger port from 17869 to 9000. + - Changed trace file naming: xdebug.trace_output_dir is now used to + configure a directory to dump automatic traces; the trace file name now + also includes the pid (xdebug.trace_output_name=pid) or a crc32 checksum + of the current working dir (xdebug.trace_output_name=crc32) and traces + are not being appended to an existing file anymore, but simply + overwritten. + - Removed $this and $GLOBALS from showing variables in the local scope. + +- Removed functions: + - xdebug_get_function_trace/xdebug_dump_function_trace() because of the new + idea of tracing. + += Fixed bugs: + - Fixed bug #89: var_dump shows empty strings garbled. + - Fixed bug #85: Xdebug segfaults when no idekey is set. + - Fixed bug #83: More than 32 parameters functions make xdebug crash. + - Fixed bug #75: xdebug's var_dump implementation is not binary safe. + - Fixed bug #73: komodo beta 4.3.7 crash. + - Fixed bug #72: breakpoint_get returns wrong structure. + - Fixed bug #69: Integer overflow in cachegrind summary. + - Fixed bug #67: Filenames in Xdebug break URI RFC with spaces. + - Fixed bug #64: Missing include of xdebug_compat.h. + - Fixed bug #57: Crash with overloading functions. + - Fixed bug #54: source command did not except missing -f parameter. + - Fixed bug #53: Feature get misusing the supported attribute. + - Fixed bug #51: Only start a debug session if XDEBUG_SESSION_START is + passed as GET or POST parameter, or the DBGP_COOKIE is send to the server. + Passing XDEBUG_SESSION_STOP as GET/POST parameter will end the debug + session and removes the cookie again. The cookie is also passed to the + remote handler backends; for DBGp it is added to the <init> packet. + - Fixed bug #49: Included file's names should not be stored by address. + - Fixed bug #44: Script time-outs should be disabled when debugging. + = Fixed bug #36: GDB handler using print causes segfault with wrong syntax + - Fixed bug #33: Implemented the use of the ZEND_POST_DEACTIVATE hook. Now we + can handle destructors safely too. + - Fixed bug #32: Unusual dynamic variables cause xdebug to crash. + + + + + 1.3.1 + 1.3.1 + + + stable + stable + + 2004-04-06 + BSD style + += Fixed profiler to aggregate class/method calls correctly. (Robert Beenen) += Fixed debugclient to initialize socket structure correctly. (Brandon Philips + and David Sklar) += GDB: Fixed bug where the source file wasn't closed after a "source" command. + (Derick) + + + + + 1.3.0 + 1.3.0 + + + stable + stable + + 2003-09-17 + BSD style + += Fixed segfault where a function name didn't exist in case of a + "call_user_function". (Derick) += Fixed reading a filename in case of an callback to a PHP function from an + internal function (like "array_map()"). (Derick) + + + + + 1.3.0rc1 + 1.3.0rc1 + + + beta + beta + + 2003-09-17 + BSD style + += Fixed bug with wrong file names for functions called from call_user_*(). + (Derick) ++ Added the option "dump_superglobals" to the remote debugger. If you set this + option to 0 the "show-local" and similar commands will not return any data + from superglobals anymore. (Derick) += Fixed bug #2: "pear package" triggers a segfault. (Derick) += Fixed crash bug when a function had sprintf style parameters (ie. + strftime()). (Derick) ++ Added "id" attribute to <var /> elements in responses from the remove + debugger when the response method is XML. This makes it possible to + distinguish between unique elements by use of recursion for example. (Derick) += Improved performance greatly by doing lazy folding of variables outside + trace mode. (Derick) += Fixed a bug with "quit", if it was used it disabled the extension for the + current process. (Derick) ++ Added the "full" argument to the remote command "backtrace". When this + argument is passed, the local variables will be returned to for each frame in + the stack. (Derick) ++ Implemented xdebug_time_index() which returns the time passed since the + start of the script. This change also changes the output of the tracing + functions as the start time will no longer be the first function call, but + the real start time of the script. (Derick) ++ Implemented the "show-local" command (shows all local variables in the + current scope including all contents). (Derick) ++ Implemented conditions for breakpoints in the "break" command. (Derick) + + + + + 1.2.0 + 1.2.0 + + + stable + stable + + 2003-04-21 + BSD style + += Fixed compilation on MacOSX. (Derick) + + + + + 1.2.0rc2 + 1.2.0rc2 + + + beta + beta + + 2003-04-15 + BSD style + += Fixed handling Windows paths in the debugger. (Derick) += Fixed getting zvals out of Zend Engine 2. (Derick) + + + + + 1.2.0rc1 + 1.2.0rc1 + + + beta + beta + + 2003-04-06 + BSD style + ++ Added code coverage functions to check which lines and how often they were + touched during execution. (Derick) ++ Made Xdebug compatible with Zend Engine 2. (Derick) ++ Added dumping of super globals on errors. (Harald Radi) ++ Added XML protocol for the debugger client. (Derick) += Fixed handling of "continue" (so that it also continues with the script). + (Derick) ++ Additions to the remote debugger: "eval" (evaluate any PHP code from the + debugger client). (Derick) ++ Added profiling support to xdebug. This introduces 3 new functions, + xdebug_start_profiling() that begins profiling process, + xdebug_stop_profiling() that ends the profiling process and + xdebug_dump_function_trace() that dumps the profiling data. (Ilia) ++ Implemented the "kill" (kills the running script) and "delete" (removes + a breakpoint on a specified element) command. (Derick) + + + + + 1.1.0 + 1.1.0 + + + stable + stable + + 2002-11-11 + BSD style + ++ Implemented the "list" (source listing), "print" (printing variable + contents), "show" (show all variables in the scope), "step" (step through + execution), "pwd" (print working directory), "next" (step over) and "finish" + (step out) commands for the remote debugger. (Derick) += Fixed lots of small bugs, under them memory leaks and crash bugs. (Derick) + + + + + 1.1.0pre2 + 1.1.0pre2 + + + beta + beta + + 2002-10-29 + BSD style + ++ Implemented class::method, object->method and file.ext:line style + breakpoints. (Derick) ++ Added xdebug.collect_params setting. If this setting is on (the default) + then Xdebug collects all parameters passed to functions, otherwise they + are not collected at all. (Derick) ++ Implemented correct handling of include/require and eval. (Derick) + + + + + 1.1.0pre1 + 1.1.0pre1 + + + beta + beta + + 2002-10-22 + BSD style + ++ Added automatic starting of function traces (xdebug.auto_trace, defaulting to + "off"). (Derick) +- Xdebug no longer supports PHP versions below PHP 4.3.0pre1. (Derick) ++ Added gdb compatible debugger handler with support for simple (function only) + breakpoints. (Derick) += Implemented a new way to get class names and file names. (Derick, Thies C. + Arntzen <thies@thieso.net>) ++ Added time-index and memory footprint to CLI dumps. (Derick) ++ Implemented remote debugger handler abstraction. (Derick) ++ Added a php3 compatible debugger handler. (Derick) + + + + + 1.0.0rc1 + 1.0.0rc1 + + + beta + beta + + 2002-09-01 + BSD style + ++ Implemented gathering of parameters to internal functions (only available + in combination with PHP 4.3.0-dev). (Derick) += Implemented a new way to get class names and file names. (Derick, Thies C. + Arntzen >thies@thieso.net<) ++ Added support for error messages with stack trace in syslog. (Sergio + Ballestrero >s.ballestrero@planetweb.it<) += Windows compilation fixes. (Derick) + + + + + 0.9.0 + 0.9.0 + + + beta + beta + + 2002-06-16 + BSD style + += Fixed a memory leak in delayed included files. (Derick) +- Added support for PHP 4.1.2. (Derick) += Rewrote xdebug_get_function_stack() and xdebug_get_function_trace() to return + data in multidimensional arrays. (Derick) += Fixed compiling without memory limit enabled (Sander Roobol, Derick) +- Add support for classnames, variable include files and variable + function names. (Derick) +- Implemented links to the PHP Manual in traces. (Derick) +- Added timestamps and memory usage to function traces. (Derick) += Fixed crash when using an user defined session handler. (Derick) ++ Implemented variable function names ($a = 'foo'; $f();) for use in + traces. (Derick) + + + + + 0.8.0 + 0.8.0 + + + beta + beta + + 2002-05-26 + BSD style + ++ Implemented much better parameter tracing for user defined + functions. (Derick) += Renamed xdebug_get_function_trace() to xdebug_dump_function_trace(). + (Derick) += Implemented new xdebug_get_function_trace() to return the function trace in + an array. (Derick) ++ Added a parameter to xdebug_start_trace(). When this parameter is used, + xdebug will dump a function trace to the filename which this parameter + speficies. (Derick) +- Fix a problem with nested member functions. (Derick) += Make configure scripts work with PHP 4.2.x. (Derick) ++ Implemented handling single-dimensional constant arrays passed to a + function. (Derick) += Fix function traces in windows. (Derick) ++ Implemented function traces, which you can start and stop with + xdebug_start_trace() and xdebug_stop_trace(). You can view the trace by using + the return array from xdebug_get_function_trace(). (Derick) += Fixed segfaults with xdebug_call_*(). (Derick) + + + + + 0.7.0 + 0.7.0 + + + beta + beta + + 2002-05-08 + BSD style + ++ Implemented handling of static method calls (foo::bar). (Derick) ++ Added correct handling of include(_once)/require(_once) and eval(). + (Derick) ++ Added ini setting to change the default setting for enabling showing + enhanced error messages. (Defaults to "On"). (Derick) ++ Added the functions xdebug_enable() and xdebug_disable() to change the + showing of stack traces from within your code. (Derick) += Fixed the extension to show all errors. (Derick) ++ Implemented xdebug_memory_usage() which returns the memory in use by PHPs + engine. (Derick) + + + + diff --git a/plugins/hwp-previews/phpcs.xml b/plugins/hwp-previews/phpcs.xml new file mode 100644 index 0000000..c9d99d3 --- /dev/null +++ b/plugins/hwp-previews/phpcs.xml @@ -0,0 +1,307 @@ + + + + + + + + Coding standards for the HWP Previews plugin + ./hwp-previews.php + ./src/ + */languages/* + */phpunit.xml* + **/tests/** + */vendor/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/hwp-previews/phpcs/HWPStandard/Sniffs/ControlStructures/ElseKeywordSniff.php b/plugins/hwp-previews/phpcs/HWPStandard/Sniffs/ControlStructures/ElseKeywordSniff.php new file mode 100644 index 0000000..98bd26d --- /dev/null +++ b/plugins/hwp-previews/phpcs/HWPStandard/Sniffs/ControlStructures/ElseKeywordSniff.php @@ -0,0 +1,47 @@ +getTokens(); + $token = $tokens[$stackPtr]; + + if ($token['code'] === T_ELSE) { + $warning = 'Usage of "else" detected; consider refactoring to avoid else branches'; + $phpcsFile->addWarning($warning, $stackPtr, 'ElseDetected'); + + return; + } + + if ($token['code'] === T_ELSEIF) { + $warning = 'Usage of "elseif" detected; consider refactoring to avoid else branches'; + $phpcsFile->addWarning($warning, $stackPtr, 'ElseIfDetected'); + } + } +} diff --git a/plugins/hwp-previews/phpcs/HWPStandard/ruleset.xml b/plugins/hwp-previews/phpcs/HWPStandard/ruleset.xml new file mode 100644 index 0000000..8947039 --- /dev/null +++ b/plugins/hwp-previews/phpcs/HWPStandard/ruleset.xml @@ -0,0 +1,4 @@ + + + HWP custom coding standard. + diff --git a/plugins/hwp-previews/phpstan.neon.dist b/plugins/hwp-previews/phpstan.neon.dist new file mode 100644 index 0000000..cdcfd02 --- /dev/null +++ b/plugins/hwp-previews/phpstan.neon.dist @@ -0,0 +1,35 @@ +includes: + - vendor/szepeviktor/phpstan-wordpress/extension.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon + +services: + - + class: HWP\Previews\PHPStan\Rules\ClassConstantVarAnnotationRule + tags: [phpstan.rules.rule] + +parameters: + # Analysis Rules + treatPhpDocTypesAsCertain: false + inferPrivatePropertyTypeFromConstructor: true + checkExplicitMixedMissingReturn: true + checkMissingTypehints: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + checkTooWideReturnTypesInProtectedAndPublicMethods: true + polluteScopeWithAlwaysIterableForeach: false + polluteScopeWithLoopInitialAssignments: false + reportAlwaysTrueInLastCondition: true + reportStaticMethodSignatures: true + reportWrongPhpDocTypeInVarTag: true + + # Configuration + level: 8 + phpVersion: + min: 70400 + max: 80400 + paths: + - hwp-previews.php + - src/ + ignoreErrors: + - + identifier: empty.notAllowed diff --git a/plugins/hwp-previews/phpstan/Rules/ClassConstantVarAnnotationRule.php b/plugins/hwp-previews/phpstan/Rules/ClassConstantVarAnnotationRule.php new file mode 100644 index 0000000..f469fb3 --- /dev/null +++ b/plugins/hwp-previews/phpstan/Rules/ClassConstantVarAnnotationRule.php @@ -0,0 +1,41 @@ + + */ +class ClassConstantVarAnnotationRule implements Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassConst::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $docComment = $node->getDocComment(); + if (!$docComment instanceof Doc) { + return [ + RuleErrorBuilder::message('Class constant must have a @var annotation in its docblock.')->build(), + ]; + } + + $docText = $docComment->getText(); + if (!preg_match('/@var\s+\S+/', $docText)) { + return [ + RuleErrorBuilder::message('Class constant docblock must contain a non-empty @var annotation.')->build(), + ]; + } + + return []; + } +} diff --git a/plugins/hwp-previews/psalm.xml b/plugins/hwp-previews/psalm.xml new file mode 100644 index 0000000..1d8acdc --- /dev/null +++ b/plugins/hwp-previews/psalm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/plugins/hwp-previews/src/Plugin.php b/plugins/hwp-previews/src/Plugin.php new file mode 100644 index 0000000..31237c7 --- /dev/null +++ b/plugins/hwp-previews/src/Plugin.php @@ -0,0 +1,471 @@ + + */ + public const SETTINGS_FIELDS = [ + self::ENABLED_FIELD => 'bool', + self::UNIQUE_POST_SLUGS_FIELD => 'bool', + self::POST_STATUSES_AS_PARENT_FIELD => 'bool', + self::PREVIEW_URL_FIELD => 'string', + self::IN_IFRAME_FIELD => 'bool', + ]; + + /** + * @var \HWP\Previews\Settings\Preview_Settings + */ + private Preview_Settings $settings; + + private Post_Types_Config_Interface $types_config; + private Post_Statuses_Config_Interface $statuses_config; + private Preview_Parameter_Registry $parameters; + + /** + * @var \HWP\Previews\Preview\Link\Preview_Link_Service + */ + private Preview_Link_Service $link_service; + private string $version; // Todo: use when enqueuing scripts. + private string $dir_path; + private static ?Plugin $instance = null; + + /** + * Constructor. + */ + private function __construct() { + $this->settings = new Preview_Settings( + new Settings_Cache_Group( self::SETTINGS_KEY, self::SETTINGS_GROUP, self::SETTINGS_FIELDS ) + ); + + $this->types_config = new Post_Types_Config(); + $this->statuses_config = new Post_Statuses_Config(); + $this->parameters = new Preview_Parameter_Registry(); + + $this->link_service = new Preview_Link_Service( + $this->types_config, + $this->statuses_config, + new Preview_Link_Placeholder_Resolver( $this->parameters ) + ); + } + + /** + * Get the instance of this class. + */ + public static function get_instance(): Plugin { + if ( self::$instance === null ) { + self::$instance = new self(); + } + + return self::$instance; + } + + public function init( string $version, string $dir_path ): void { + $this->version = $version; + $this->dir_path = $dir_path; + + // Init core functionality. + $this->init_core_functionality(); + + // Settings. + $this->register_settings_pages(); + $this->register_settings_fields(); + + // Functionality. + $this->enable_unique_post_slug(); + $this->enable_post_statuses_as_parent(); + $this->enable_preview_in_iframe(); + $this->enable_preview_functionality(); + } + + public function enable_unique_post_slug(): void { + $post_slug_manger = new Post_Slug_Manager( + $this->types_config, + $this->statuses_config, + new Post_Slug_Repository() + ); + + add_filter( 'wp_insert_post_data', function ( $data, $postarr ) use ( $post_slug_manger ) { + $post = new WP_Post( new Post_Data_Model( $data, (int) ( $postarr['ID'] ?? 0 ) ) ); + + // Check if the correspondent setting is enabled. + if ( ! $this->settings->unique_post_slugs( $post->post_type ) ) { + return $data; + } + + $post_slug = $post_slug_manger->force_unique_post_slug( $post ); + + if ( ! empty( $post_slug ) ) { + $data['post_name'] = $post_slug; + } + + return $data; + }, 10, 2 ); + } + + private function init_core_functionality(): void { + add_action( 'init', function (): void { + + // Register default preview parameters. + $this->setup_default_preview_parameters(); + + // Setup post types configuration. + $this->types_config->set_post_types( + $this->settings->post_types_enabled() + ); + + // Setup post statuses configuration. + $this->statuses_config->set_post_statuses( + [ 'publish', 'future', 'draft', 'pending', 'private' ] + ); + + /** + * Allows modify parameters registry. + */ + do_action( 'hwp_previews_parameters_registry', $this->parameters ); + }, 5, 0 ); + } + + /** + * Registers default preview parameters on the init hook. + * Uses 'hwp_previews_parameters_registry' action to allow modification of the parameters registry. + * + * @return void + */ + private function setup_default_preview_parameters(): void { + $this->parameters + ->register( + new Preview_Parameter( 'ID', static fn( WP_Post $post ) => (string) $post->ID, __( 'Post ID.', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'author_ID', static fn( WP_Post $post ) => $post->post_author, __( 'ID of post author..', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'status', static fn( WP_Post $post ) => $post->post_status, __( 'The post\'s status..', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'slug', static fn( WP_Post $post ) => $post->post_name, __( 'The post\'s slug.', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'parent_ID', static fn( WP_Post $post ) => (string) $post->post_parent, __( 'ID of a post\'s parent post.', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'type', static fn( WP_Post $post ) => $post->post_type, __( 'The post\'s type, like post or page.', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'uri', static fn( WP_Post $post ) => (string) get_page_uri( $post ), __( 'The URI path for a page.', 'hwp-previews' ) ) + )->register( + new Preview_Parameter( 'template', static fn( WP_Post $post ) => (string) get_page_template_slug( $post ), __( 'Specific template filename for a given post.', 'hwp-previews' ) ) + ); + } + + private function register_settings_pages(): void { + add_action( 'admin_menu', function (): void { + /** + * @var array $post_types Array of post types where key is the post type slug and value is the label. + */ + $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', Helpers::get_public_post_types() ); + + $this->create_settings_page( $post_types )->register_page(); + $this->create_settings_subpage()->register_page(); + } ); + } + + private function register_settings_fields(): void { + add_action( 'admin_init', function (): void { + + /** + * @var array $post_types Array of post types where key is the post type slug and value is the label. + */ + $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', Helpers::get_public_post_types() ); + + /** + * Register setting itself. + */ + $this->create_tabbed_settings( $post_types )->register_settings(); + + /** + * Register settings sections and fields for each post type. + */ + foreach ( $post_types as $post_type => $label ) { + $this->create_setting_section( $post_type, $label )->register_section( self::SETTINGS_KEY, $post_type, "hwp-previews-{$post_type}" ); + } + } ); + } + + private function enable_post_statuses_as_parent(): void { + $post_parent_manager = new Post_Parent_Manager( $this->types_config, $this->statuses_config ); + + $post_parent_manager_callback = function ( array $args ) use ( $post_parent_manager ): array { + if ( empty( $args['post_type'] ) ) { + return $args; + } + + // Check if the correspondent setting is enabled. + if ( ! $this->settings->post_statuses_as_parent( (string) $args['post_type'] ) ) { + return $args; + } + + $post_type = get_post_type_object( (string) $args['post_type'] ); + if ( $post_type instanceof WP_Post_Type ) { + $args['post_status'] = $post_parent_manager->get_post_statuses_as_parent( $post_type ); + } + + return $args; + }; + + add_filter( 'page_attributes_dropdown_pages_args', $post_parent_manager_callback ); + add_filter( 'quick_edit_dropdown_pages_args', $post_parent_manager_callback ); + + // And for Gutenberg. + foreach ( $this->types_config->get_post_types() as $post_type ) { + $post_type_object = get_post_type_object( $post_type ); + if ( ! $post_type_object instanceof WP_Post_Type || ! $this->types_config->supports_gutenberg( $post_type_object ) ) { + continue; + } + add_filter( 'rest_' . $post_type . '_query', $post_parent_manager_callback ); + } + } + + private function enable_preview_in_iframe(): void { + $template_resolver = new Preview_Template_Resolver( $this->types_config, $this->statuses_config ); + + add_filter( 'template_include', function ( $template ) use ( $template_resolver ) { + if ( ! is_preview() ) { + return $template; + } + + $post = get_post(); + if ( ! $post instanceof WP_Post ) { + return $template; + } + + // Check if the correspondent setting is enabled. + if ( ! $this->settings->in_iframe( $post->post_type ) ) { + return $template; + } + + /** + * The filter 'hwp_previews_template_dir_path' allows to change the template directory path. + */ + $template_dir_path = (string) apply_filters( + 'hwp_previews_template_dir_path', + $this->dir_path . 'templates/hwp-preview.php' + ); + + $preview_template = $template_resolver->resolve_template_path( $post, $template_dir_path ); + + if ( empty( $preview_template ) ) { + return $template; + } + + set_query_var( $template_resolver::HWP_PREVIEWS_IFRAME_PREVIEW_URL, $this->generate_preview_url( $post ) ); + + return $preview_template; + }, 999 ); + } + + private function enable_preview_functionality(): void { + add_filter( 'preview_post_link', function ( $link, $post ) { + // If iframe option is enabled, we need to resolve preview on the template redirect level. + if ( $this->settings->in_iframe( $post->post_type ) ) { + return $link; + } + + return $this->generate_preview_url( $post ) ?: $link; + }, 10, 2 ); + + /** + * Hack Function that changes the preview link for draft articles, + * this must be removed when properly fixed https://github.com/WordPress/gutenberg/issues/13998. + */ + foreach ( $this->types_config->get_post_types() as $post_type ) { + add_filter( 'rest_prepare_' . $post_type, function ( $response, $post ) { + // If iframe option is enabled, we need to resolve preview on the template redirect level. + if ( $this->settings->in_iframe( $post->post_type ) ) { + return $response; + } + + $preview_url = $this->generate_preview_url( $post ); + if ( $preview_url ) { + $response->data['link'] = $preview_url; + } + + return $response; + }, 10, 2 ); + } + } + + private function generate_preview_url( WP_Post $post ): string { + $url = $this->settings->url_template( $post->post_type ); + + if ( ! empty( $url ) ) { + return $this->link_service->generate_preview_post_link( $url, $post ); + } + + return ''; + } + + /** + * @param array $post_types + * + * @return \HWP\Previews\Settings\Menu\Menu_Page + */ + private function create_settings_page( array $post_types ): Menu_Page { + return new Menu_Page( + __( 'HWP Previews Settings', 'hwp-previews' ), + 'HWP Previews', + 'hwp-previews', + $this->dir_path . 'templates/admin/settings-page-main.php', + [ + self::SETTINGS_ARGS => [ + 'tabs' => $post_types, + 'current_tab' => isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : key( $post_types ), + 'params' => $this->parameters->get_descriptions(), + ], + ], + 'dashicons-welcome-view-site' + ); + } + + private function create_settings_subpage(): Submenu_Page { + return new Submenu_Page( + 'hwp-previews', + __( 'Testing Tool', 'hwp-previews' ), + 'Testing Tool', + 'hwp-previews-testing-tool', + $this->dir_path . 'templates/admin/settings-page-testing-tool.php' + ); + } + + /** + * @param array $post_types + * + * @return \HWP\Previews\Settings\Tabbed_Settings + */ + private function create_tabbed_settings( array $post_types ): Tabbed_Settings { + return new Tabbed_Settings( + self::SETTINGS_GROUP, + self::SETTINGS_KEY, + array_keys( $post_types ), // Items allowed, represented as a tabs. + self::SETTINGS_FIELDS + ); + } + + private function create_setting_section( string $post_type, string $label ): Settings_Section { + return new Settings_Section( + 'hwp_previews_section_' . $post_type, + '', + 'hwp-previews-' . $post_type, + $this->create_settings_fields( $post_type, $label, is_post_type_hierarchical( $post_type ) ) + ); + } + + /** + * @param string $post_type + * @param string $label + * @param bool $is_hierarchical + * + * @return array<\HWP\Previews\Settings\Fields\Abstract_Settings_Field> + */ + private function create_settings_fields( string $post_type, string $label, bool $is_hierarchical ): array { + $fields = []; + + foreach ( + [ + 'enabled' => sprintf( __( 'Enable HWP Previews for %s', 'hwp-previews' ), $label ), + 'unique_post_slugs' => __( 'Enable unique post slugs for all post statuses', 'hwp-previews' ), + 'post_statuses_as_parent' => __( 'Allow all post statuses in parents option', 'hwp-previews' ), + 'in_iframe' => sprintf( __( 'Load previews in iframe', 'hwp-previews' ), $label ), + ] as $id => $description + ) { + $fields[ $id ] = new Checkbox_Field( $id, $description ); + } + + // Remove the 'post_statuses_as_parent' field if the post type is not hierarchical. + if ( ! $is_hierarchical ) { + unset( $fields['post_statuses_as_parent'] ); + } + + // Preview URL field. + $fields[] = new Text_Input_Field( + 'preview_url', + sprintf( __( 'Preview URL for %s', 'hwp-previews' ), $label ), + "https://example.com/{$post_type}?preview=true&post_id={ID}&name={slug}", + 'large-text code hwp-previews-url' // The class is being used as a query for the JS. + ); + + return $fields; + } + +} diff --git a/plugins/hwp-previews/src/Post/Data/Post_Data_Model.php b/plugins/hwp-previews/src/Post/Data/Post_Data_Model.php new file mode 100644 index 0000000..d1c54e1 --- /dev/null +++ b/plugins/hwp-previews/src/Post/Data/Post_Data_Model.php @@ -0,0 +1,63 @@ + $data Array of data to hydrate the model. + * @param int $post_id Post ID. + */ + public function __construct( array $data, int $post_id = 0 ) { + $this->ID = (int) ( $data['ID'] ?? $post_id ); + $this->post_status = (string) ( $data['post_status'] ?? '' ); + $this->post_type = (string) ( $data['post_type'] ?? '' ); + $this->post_name = (string) ( $data['post_name'] ?? '' ); + $this->post_title = (string) ( $data['post_title'] ?? '' ); + } + +} diff --git a/plugins/hwp-previews/src/Post/Parent/Contracts/Post_Parent_Manager_Interface.php b/plugins/hwp-previews/src/Post/Parent/Contracts/Post_Parent_Manager_Interface.php new file mode 100644 index 0000000..77fecdf --- /dev/null +++ b/plugins/hwp-previews/src/Post/Parent/Contracts/Post_Parent_Manager_Interface.php @@ -0,0 +1,20 @@ + + */ + public function get_post_statuses_as_parent( WP_Post_Type $post_type ): array; + +} diff --git a/plugins/hwp-previews/src/Post/Parent/Post_Parent_Manager.php b/plugins/hwp-previews/src/Post/Parent/Post_Parent_Manager.php new file mode 100644 index 0000000..d9871fd --- /dev/null +++ b/plugins/hwp-previews/src/Post/Parent/Post_Parent_Manager.php @@ -0,0 +1,69 @@ + + */ + public const POST_STATUSES = [ 'publish', 'future', 'draft', 'pending', 'private' ]; + + /** + * Post types configuration. + * + * @var \HWP\Previews\Post\Type\Contracts\Post_Types_Config_Interface + */ + private Post_Types_Config_Interface $post_types; + + /** + * Post statuses configuration. + * + * @var \HWP\Previews\Post\Status\Contracts\Post_Statuses_Config_Interface + */ + private Post_Statuses_Config_Interface $post_statuses; + + /** + * Post_Parent_Manager constructor. + * + * @param \HWP\Previews\Post\Type\Contracts\Post_Types_Config_Interface $post_types Post types configuration. + * @param \HWP\Previews\Post\Status\Contracts\Post_Statuses_Config_Interface $post_statuses Post statuses configuration. + */ + public function __construct( Post_Types_Config_Interface $post_types, Post_Statuses_Config_Interface $post_statuses ) { + $this->post_types = $post_types; + $this->post_statuses = $post_statuses; + } + + /** + * Get the post statuses that can be used as parent for a given post type. + * + * @param \WP_Post_Type $post_type The post type object. + * + * @return array + */ + public function get_post_statuses_as_parent( WP_Post_Type $post_type ): array { + if ( + ! $this->post_types->is_post_type_applicable( $post_type->name ) || + ! $this->post_types->is_hierarchical( $post_type ) + ) { + return []; + } + + return array_intersect( self::POST_STATUSES, $this->post_statuses->get_post_statuses() ); + } + +} diff --git a/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Manager_Interface.php b/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Manager_Interface.php new file mode 100644 index 0000000..fdb2b03 --- /dev/null +++ b/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Manager_Interface.php @@ -0,0 +1,35 @@ + $reserved_slugs Array of reserved slugs. + * + * @return string + */ + public function generate_unique_slug( string $slug, string $post_type, int $post_id, array $reserved_slugs ): string; + +} diff --git a/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Repository_Interface.php b/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Repository_Interface.php new file mode 100644 index 0000000..8c8b95a --- /dev/null +++ b/plugins/hwp-previews/src/Post/Slug/Contracts/Post_Slug_Repository_Interface.php @@ -0,0 +1,18 @@ +types = $types; + $this->statuses = $statuses; + $this->slug_repository = $slug_repository; + } + + /** + * Forces unique slug for a post. + * + * @param \WP_Post $post The post object. + * + * @return string The unique slug. + */ + public function force_unique_post_slug( WP_Post $post ): string { + if ( + ! (bool) $post->ID || + ! $this->types->is_post_type_applicable( $post->post_type ) || + ! $this->statuses->is_post_status_applicable( $post->post_status ) + ) { + return ''; + } + + global $wp_rewrite; + + $slug = empty( $post->post_name ) ? sanitize_title( $post->post_title, "$post->post_status-$post->ID" ) : $post->post_name; + $feeds = is_array( $wp_rewrite->feeds ) ? $wp_rewrite->feeds : []; + + return $this->generate_unique_slug( $slug, $post->post_type, $post->ID, array_merge( $feeds, [ 'embed' ] ) ); + } + + /** + * Generates a unique slug for a post. + * + * @see wp-includes/post.php + * + * @param string $slug . + * @param string $post_type . + * @param int $post_id . + * @param array $reserved_slugs . + * + * @return string + */ + public function generate_unique_slug( string $slug, string $post_type, int $post_id, array $reserved_slugs ): string { + if ( empty( $slug ) ) { + $slug = 'undefined'; + } + + if ( ! $this->slug_repository->is_slug_taken( $slug, $post_type, $post_id ) && ! in_array( $slug, $reserved_slugs, true ) ) { + return $slug; + } + + $suffix = 2; + do { + $new_slug = _truncate_post_slug( $slug, 200 - ( strlen( (string) $suffix ) + 1 ) ) . "-$suffix"; + ++$suffix; + } while ( $this->slug_repository->is_slug_taken( $new_slug, $post_type, $post_id ) ); + + return $new_slug; + } + +} diff --git a/plugins/hwp-previews/src/Post/Slug/Post_Slug_Repository.php b/plugins/hwp-previews/src/Post/Slug/Post_Slug_Repository.php new file mode 100644 index 0000000..01b5a30 --- /dev/null +++ b/plugins/hwp-previews/src/Post/Slug/Post_Slug_Repository.php @@ -0,0 +1,41 @@ +get_var( // phpcs:ignore WordPress.DB + $wpdb->prepare( + "SELECT post_name FROM {$wpdb->posts} WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1", + $slug, + $post_type, + $post_id + ) + ); + } + +} diff --git a/plugins/hwp-previews/src/Post/Status/Contracts/Post_Statuses_Config_Interface.php b/plugins/hwp-previews/src/Post/Status/Contracts/Post_Statuses_Config_Interface.php new file mode 100644 index 0000000..eafd1b6 --- /dev/null +++ b/plugins/hwp-previews/src/Post/Status/Contracts/Post_Statuses_Config_Interface.php @@ -0,0 +1,30 @@ + $post_statuses The post statuses to set. + */ + public function set_post_statuses( array $post_statuses ): self; + + /** + * Get the post statuses that are applicable for the plugin. + * + * @return array + */ + public function get_post_statuses(): array; + + /** + * Check if a given post status is applicable for the plugin. + * + * @param string $post_status Post status slug. + */ + public function is_post_status_applicable( string $post_status ): bool; + +} diff --git a/plugins/hwp-previews/src/Post/Status/Post_Statuses_Config.php b/plugins/hwp-previews/src/Post/Status/Post_Statuses_Config.php new file mode 100644 index 0000000..2644be8 --- /dev/null +++ b/plugins/hwp-previews/src/Post/Status/Post_Statuses_Config.php @@ -0,0 +1,54 @@ + + */ + private array $post_statuses = []; + + /** + * Sets the post statuses that are applicable for the plugin. + * + * @param array $post_statuses Post statuses that are applicable for the plugin. + * + * @return $this + */ + public function set_post_statuses( array $post_statuses ): self { + $this->post_statuses = $post_statuses; + + return $this; + } + + /** + * Get the post statuses that are applicable for the plugin. + * + * @return array Post statuses. + */ + public function get_post_statuses(): array { + return $this->post_statuses; + } + + /** + * TODO: add post status verification to support custom post types in future. Or anything else. + * + * @param string $post_status Post status to check. + * + * @return bool + */ + public function is_post_status_applicable( string $post_status ): bool { + return in_array( $post_status, $this->post_statuses, true ); + } + +} diff --git a/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php b/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php new file mode 100644 index 0000000..4585d2e --- /dev/null +++ b/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php @@ -0,0 +1,53 @@ + $post_types The post type to check. + */ + public function set_post_types( array $post_types ): self; + + /** + * Get the post types that are applicable for previews. + * + * @return array Post types that are applicable for previews. + */ + public function get_post_types(): array; + + /** + * Check if a post type is applicable for previews. + * + * @param string $post_type The post type to check. + */ + public function is_post_type_applicable( string $post_type ): bool; + + /** + * Check if a post type is hierarchical. + * + * @param \WP_Post_Type $post_type The post type object. + */ + public function is_hierarchical( WP_Post_Type $post_type ): bool; + + /** + * Check if a post type supports Gutenberg. + * + * @param \WP_Post_Type $post_type The post type object. + */ + public function supports_gutenberg( WP_Post_Type $post_type ): bool; + + /** + * Gets all publicly available post types as key value array, where key is a post type slug and value is a label. + * + * @return array + */ + public function get_publicly_available_post_types(): array; + +} diff --git a/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php b/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php new file mode 100644 index 0000000..dba967d --- /dev/null +++ b/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php @@ -0,0 +1,107 @@ + + */ + private array $post_types = []; + + /** + * Sets the post types that are applicable for preview links. + * + * @param array $post_types Post types that are applicable for preview links. + * + * @return $this + */ + public function set_post_types( array $post_types ): self { + $this->post_types = $post_types; + + return $this; + } + + /** + * Get the post types that are applicable for preview links. + * + * @return array + */ + public function get_post_types(): array { + return $this->post_types; + } + + /** + * Check if the post type is applicable for preview links. + * + * @param string $post_type Post type to check. + * + * @return bool + */ + public function is_post_type_applicable( string $post_type ): bool { + return in_array( $post_type, $this->post_types, true ) && post_type_exists( $post_type ); + } + + /** + * Check if the post type is hierarchical. + * + * @param \WP_Post_Type $post_type Post type object. + * + * @return bool + */ + public function is_hierarchical( WP_Post_Type $post_type ): bool { + return $post_type->hierarchical; + } + + /** + * Check if the post type supports Gutenberg editor. + * + * @param \WP_Post_Type $post_type Post type object. + * + * @return bool + */ + public function supports_gutenberg( WP_Post_Type $post_type ): bool { + if ( + empty( $post_type->show_in_rest ) || + empty( $post_type->supports ) || + ! is_array( $post_type->supports ) || + ! in_array( 'editor', $post_type->supports, true ) + ) { + return false; + } + + if ( ! is_plugin_active( 'classic-editor/classic-editor.php' ) ) { + return true; + } + + $classic_editor_settings = (array) get_option( 'classic-editor-settings', [] ); + + return ! ( + ! empty( $classic_editor_settings['post_types'] ) && + is_array( $classic_editor_settings['post_types'] ) && + in_array( $post_type->name, $classic_editor_settings['post_types'], true ) + ); + } + + /** + * Gets all publicly available post types as key value array, where key is a post type slug and value is a label. + * + * @return array + */ + public function get_publicly_available_post_types(): array { + $post_type_objects = get_post_types( [ 'public' => true ], 'objects' ); + + return wp_list_pluck( $post_type_objects, 'label', 'name' ); + } + +} diff --git a/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php new file mode 100644 index 0000000..bd75133 --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php @@ -0,0 +1,76 @@ +registry = $registry; + } + + /** + * Replace all {PLACEHOLDER} tokens in template string with urlencoded string values from callbacks. + * + * @param string $template The string containing {KEY} placeholders. + * @param \WP_Post $post The post object to resolve the tokens against. + * + * @return string + */ + public function resolve_placeholders(string $template, WP_Post $post ): string { + return preg_replace_callback( + self::PLACEHOLDER_REGEX, + fn(array $matches): string => rawurlencode( $this->resolve_token( $matches[1], $post ) ), + $template + ); + } + + /** + * Resolve individual token by key. + * + * @param string $key The token key without braces. + * @param \WP_Post $post Post object to resolve the token against. + * + * @return string + */ + private function resolve_token( string $key, WP_Post $post ): string { + $parameter = $this->registry->get( $key ); + if ( ! $parameter instanceof Preview_Parameter_Interface ) { + return self::PLACEHOLDER_NOT_FOUND; + } + + return $parameter->get_value( $post ); + } + +} diff --git a/plugins/hwp-previews/src/Preview/Link/Preview_Link_Service.php b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Service.php new file mode 100644 index 0000000..094327c --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Service.php @@ -0,0 +1,74 @@ +types = $types; + $this->statuses = $statuses; + $this->resolver = $resolver; + } + + /** + * Generate a preview post link. + * + * @param string $preview_url_template Preview URL template. + * @param \WP_Post $post The post object. + * + * @return string + */ + public function generate_preview_post_link( string $preview_url_template, WP_Post $post ): string { + if ( + empty( $preview_url_template ) || + ! $this->types->is_post_type_applicable( $post->post_type ) || + ! $this->statuses->is_post_status_applicable( $post->post_status ) + ) { + return ''; + } + + return $this->resolver->resolve_placeholders( $preview_url_template, $post ); + } + +} diff --git a/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Builder_Interface.php b/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Builder_Interface.php new file mode 100644 index 0000000..d025728 --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Builder_Interface.php @@ -0,0 +1,27 @@ + + */ + public function build_preview_args( WP_Post $post, string $page_uri, string $token ): array; + +} diff --git a/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Interface.php b/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Interface.php new file mode 100644 index 0000000..6db8d07 --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Parameter/Contracts/Preview_Parameter_Interface.php @@ -0,0 +1,41 @@ +name = $name; + $this->description = $description; + $this->callback = $callback; + } + + /** + * Get the name of the parameter. + * + * @inheritDoc + */ + public function get_name(): string { + return $this->name; + } + + /** + * Get the description of the parameter. + * + * @inheritDoc + */ + public function get_description(): string { + return $this->description; + } + + /** + * Get the value of the parameter for a given post. + * No need to URL-encode here. + * + * @param \WP_Post $post The post object. + * + * @return string + */ + public function get_value( WP_Post $post ): string { + return call_user_func( $this->callback, $post ); + } + +} diff --git a/plugins/hwp-previews/src/Preview/Parameter/Preview_Parameter_Registry.php b/plugins/hwp-previews/src/Preview/Parameter/Preview_Parameter_Registry.php new file mode 100644 index 0000000..925d25c --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Parameter/Preview_Parameter_Registry.php @@ -0,0 +1,85 @@ + + */ + private array $parameters = []; + + /** + * Register a parameter. + * + * @param \HWP\Previews\Preview\Parameter\Contracts\Preview_Parameter_Interface $parameter The parameter object. + * + * @return self + */ + public function register( Preview_Parameter_Interface $parameter ): self { + $this->parameters[ $parameter->get_name() ] = $parameter; + + return $this; + } + + /** + * Unregister a parameter. + * + * @param string $name The parameter name. + * + * @return self + */ + public function unregister( string $name ): self { + if ( isset( $this->parameters[ $name ] ) ) { + unset( $this->parameters[ $name ] ); + } + + return $this; + } + + /** + * Get all registered parameters. + * + * @return array + */ + public function get_all(): array { + return $this->parameters; + } + + /** + * Get all registered parameters as an array of their names and descriptions. + * + * @return array + */ + public function get_descriptions(): array { + $descriptions = []; + foreach ( $this->parameters as $parameter ) { + $descriptions[ $parameter->get_name() ] = $parameter->get_description(); + } + + return $descriptions; + } + + /** + * Get a specific parameter by name. Returns null if not found. + * + * @param string $name The parameter name. + * + * @return \HWP\Previews\Preview\Parameter\Contracts\Preview_Parameter_Interface|null + */ + public function get( string $name ): ?Preview_Parameter_Interface { + return $this->parameters[ $name ] ?? null; + } + +} diff --git a/plugins/hwp-previews/src/Preview/Template/Contracts/Preview_Template_Resolver_Interface.php b/plugins/hwp-previews/src/Preview/Template/Contracts/Preview_Template_Resolver_Interface.php new file mode 100644 index 0000000..0daa850 --- /dev/null +++ b/plugins/hwp-previews/src/Preview/Template/Contracts/Preview_Template_Resolver_Interface.php @@ -0,0 +1,21 @@ +types = $types; + $this->statuses = $statuses; + } + + /** + * Resolves the template path for the preview. + * + * @param \WP_Post $post The post object. + * @param string $template_path The template path. + * + * @return string The resolved template path. + */ + public function resolve_template_path( WP_Post $post, string $template_path ): string { + if ( + empty( $template_path ) || + ! $this->types->is_post_type_applicable( $post->post_type ) || + ! $this->statuses->is_post_status_applicable( $post->post_status ) || + ! is_preview() + ) { + return ''; + } + + return file_exists( $template_path ) ? $template_path : ''; + } + +} diff --git a/plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php b/plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php new file mode 100644 index 0000000..b3858e3 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php @@ -0,0 +1,58 @@ + $default_value Default post types. + * + * @return array + */ + public function post_types_enabled( array $default_value = [] ): array; + + /** + * Gets URL template for the given post type. + * + * @param string $post_type Post type slug. + * @param string $default_value Default URL template. + * + * @return string + */ + public function url_template( string $post_type, string $default_value = '' ): string; + + /** + * If the post type post statuses should have unique slug for the post type. + * + * @param string $post_type Post type slug. + * @param bool $default_value Default value. + * + * @return bool + */ + public function unique_post_slugs( string $post_type, bool $default_value = false ): bool; + + /** + * It the specified post statuses should be allowed to be used as parent post statuses. + * + * @param string $post_type Post type slug. + * @param bool $default_value Default value. + * + * @return bool + */ + public function post_statuses_as_parent( string $post_type, bool $default_value = false ): bool; + + /** + * If the post type preview supposed to be opened in iframe on WP Admin side. + * + * @param string $post_type Post type slug. + * @param bool $default_value Default value. + * + * @return bool + */ + public function in_iframe( string $post_type, bool $default_value = false ): bool; + +} diff --git a/plugins/hwp-previews/src/Settings/Contracts/General_Settings_Interface.php b/plugins/hwp-previews/src/Settings/Contracts/General_Settings_Interface.php new file mode 100644 index 0000000..1937e60 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Contracts/General_Settings_Interface.php @@ -0,0 +1,23 @@ + $option_value Settings value. + * @param string $setting_key The settings key. + * @param string $post_type The post type. + * + * @return void + */ + abstract protected function render_field( array $option_value, string $setting_key, string $post_type ): void; + + /** + * Constructor. + * + * @param string $id The settings field ID. + * @param string $title The settings field title. + * @param string $css_class The settings field class. + */ + public function __construct( + string $id, + string $title, + string $css_class = '' + ) { + $this->id = $id; + $this->title = $title; + $this->class = $css_class; + } + + /** + * Register the settings field. + * + * @param string $settings_key The settings key. + * @param string $section The settings section. + * @param string $post_type The post type. + * @param string $page The settings page. + * + * @return void + */ + public function register_settings_field( string $settings_key, string $section, string $post_type, string $page ): void { + add_settings_field( + $this->id, + $this->title, + [ $this, 'settings_field_callback' ], + $page, + $section, + [ + 'settings_key' => $settings_key, + 'post_type' => $post_type, + ] + ); + } + + /** + * Callback for the settings field. + * + * @param array $args The settings field arguments. + * + * @return void + */ + public function settings_field_callback( array $args ): void { + $settings_key = (string) ( $args['settings_key'] ?? '' ); + $post_type = (string) ( $args['post_type'] ?? '' ); + $value = $this->get_setting_value( $settings_key, $post_type ); + + $this->render_field( $value, $settings_key, $post_type ); + } + + /** + * Get the settings value. + * + * @param string $settings_key The settings key. + * @param string $post_type The post type. + * + * @return array + */ + private function get_setting_value( string $settings_key, string $post_type ): array { + $value = get_option( $settings_key, [] ); + + if ( + empty( $value ) || + ! isset( $value[ $post_type ] ) || + ! is_array( $value[ $post_type ] ) + ) { + return []; + } + + return $value[ $post_type ]; + } + +} diff --git a/plugins/hwp-previews/src/Settings/Fields/Checkbox_Field.php b/plugins/hwp-previews/src/Settings/Fields/Checkbox_Field.php new file mode 100644 index 0000000..38505b9 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Fields/Checkbox_Field.php @@ -0,0 +1,54 @@ +default = $default_value; + } + + /** + * Render the checkbox settings field. + * + * @param array $option_value Settings value. + * @param string $setting_key The settings key. + * @param string $post_type The post type. + * + * @return void + */ + protected function render_field( $option_value, $setting_key, $post_type ): void { + $enabled = isset( $option_value[ $this->id ] ) + ? (bool) $option_value[ $this->id ] + : $this->default; + + printf( + '', + esc_attr( $setting_key ), + esc_attr( $post_type ), + esc_attr( $this->id ), + checked( 1, $enabled, false ), + sanitize_html_class( $this->class ) + ); + } + +} diff --git a/plugins/hwp-previews/src/Settings/Fields/Text_Input_Field.php b/plugins/hwp-previews/src/Settings/Fields/Text_Input_Field.php new file mode 100644 index 0000000..c012329 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Fields/Text_Input_Field.php @@ -0,0 +1,51 @@ +default = $default_value; + } + + /** + * Render the field. + * + * @param array $option_value The value of the field. + * @param string $setting_key The settings key. + * @param string $post_type The post type. + * + * @return void + */ + protected function render_field( array $option_value, string $setting_key, string $post_type ): void { + printf( + '', + esc_attr( $setting_key ), + esc_attr( $post_type ), + esc_attr( $this->id ), + esc_attr( (string) ( $option_value[ $this->id ] ?? $this->default ) ), + esc_attr( $this->default ), + esc_attr( $this->class ) + ); + } + +} diff --git a/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php b/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php new file mode 100644 index 0000000..b7b1728 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php @@ -0,0 +1,125 @@ +> + */ + protected array $args; + + /** + * The name of a Dashicons helper class to use a font icon. + * + * @var string + */ + protected string $icon; + + /** + * Constructor. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. + * @param string $menu_title The text to be used for the menu. + * @param string $menu_slug The slug name to refer to this menu by. Should be unique for this menu and only include lowercase alphanumeric, dashes, and underscores characters to be compatible with sanitize_key(). + * @param string $template The template that will be included in the callback. + * @param array> $args The args array for the template. + * @param string $icon The name of a Dashicons helper class to use a font icon. + */ + public function __construct( + string $page_title, + string $menu_title, + string $menu_slug, + string $template, + array $args = [], + string $icon = 'dashicons-admin-generic' + ) { + $this->page_title = $page_title; + $this->menu_title = $menu_title; + $this->menu_slug = $menu_slug; + $this->template = $template; + $this->args = $args; + $this->icon = $icon; + } + + /** + * Registers the menu page in the WordPress admin. + * + * @return void + */ + public function register_page(): void { + add_menu_page( + $this->page_title, + $this->menu_title, + 'manage_options', + $this->menu_slug, + [ $this, 'registration_callback' ], + $this->icon + ); + } + + /** + * Callback function to display the content of the menu page. + * + * @return void + */ + public function registration_callback(): void { + if ( empty( $this->template ) || ! file_exists( $this->template ) ) { + printf( + '

%s

', + __( 'The HWP Previews Settings template does not exist.', 'hwp-previews' ) + ); + + return; + } + $this->set_query_vars(); + include_once $this->template; + } + + /** + * Sets the query vars for the template. + * + * @return void + */ + protected function set_query_vars(): void { + foreach ( $this->args as $query_var => $args ) { + set_query_var( $query_var, $args ); + } + } + +} diff --git a/plugins/hwp-previews/src/Settings/Menu/Submenu_Page.php b/plugins/hwp-previews/src/Settings/Menu/Submenu_Page.php new file mode 100644 index 0000000..989f7a8 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Menu/Submenu_Page.php @@ -0,0 +1,54 @@ + $args An array of arguments to be passed to the template. + */ + public function __construct( + string $parent_slug, + string $page_title, + string $menu_title, + string $menu_slug, + string $template, + array $args = [] + ) { + $this->parent_slug = $parent_slug; + parent::__construct( $page_title, $menu_title, $menu_slug, $template, $args ); + } + + /** + * Register the submenu page. Should be called on the 'admin_menu' action. + * + * @return void + */ + public function register_page(): void { + add_submenu_page( + $this->parent_slug, + $this->page_title, + $this->menu_title, + 'manage_options', + $this->menu_slug, + [ $this, 'registration_callback' ] + ); + } + +} diff --git a/plugins/hwp-previews/src/Settings/Preview_Settings.php b/plugins/hwp-previews/src/Settings/Preview_Settings.php new file mode 100644 index 0000000..5d4d841 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Preview_Settings.php @@ -0,0 +1,91 @@ +group = $group; + } + + /** + * Get all post types that are enabled in the settings. + * + * @param array $default_value Default post types to return if none are enabled. + * + * @return array + */ + public function post_types_enabled( array $default_value = [] ): array { + $value = $this->group->get_cache_settings(); + + $post_types = array_filter( $value, static fn( $item ) => isset( $item[ Plugin::ENABLED_FIELD ] ) && $item[ Plugin::ENABLED_FIELD ] === true ); + + return ! empty( $post_types ) ? array_keys( $post_types ) : $default_value; + } + + /** + * Get Unique Post Slugs setting value for the given post type. + * + * @param string $post_type The post type to get the setting for. + * @param bool $default_value The default value to return if the setting is not set. + * + * @return bool + */ + public function unique_post_slugs( string $post_type, bool $default_value = false ): bool { + return $this->group->get_bool( Plugin::UNIQUE_POST_SLUGS_FIELD, $post_type, $default_value ); + } + + /** + * Get Post Statuses as Parent setting value for the given post type. + * + * @param string $post_type The post type to get the setting for. + * @param bool $default_value The default value to return if the setting is not set. + * + * @return bool + */ + public function post_statuses_as_parent( string $post_type, bool $default_value = false ): bool { + return $this->group->get_bool( Plugin::POST_STATUSES_AS_PARENT_FIELD, $post_type, $default_value ); + } + + /** + * Show In iframe value for the given post type. + * + * @param string $post_type The post type to get the setting for. + * @param bool $default_value The default value to return if the setting is not set. + * + * @return bool + */ + public function in_iframe( string $post_type, bool $default_value = false ): bool { + return $this->group->get_bool( Plugin::IN_IFRAME_FIELD, $post_type, $default_value ); + } + + /** + * URL template setting value for the given post type. + * + * @param string $post_type The post type to get the setting for. + * @param string $default_value The default value to return if the setting is not set. + * + * @return string + */ + public function url_template( string $post_type, string $default_value = '' ): string { + return $this->group->get_string( Plugin::PREVIEW_URL_FIELD, $post_type, $default_value ); + } + +} diff --git a/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php b/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php new file mode 100644 index 0000000..fe961fd --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php @@ -0,0 +1,113 @@ + + */ + private array $settings_config; + + /** + * Constructor. + * Adds a settings group to the list of non-persistent groups. + * + * @param string $option Option name. + * @param string $group Group name. + * @param array $settings_config Array of settings config where keys are allowed options and values are types. + */ + public function __construct( string $option, string $group, array $settings_config ) { + $this->option = $option; + $this->group = $group; + $this->settings_config = $settings_config; + + wp_cache_add_non_persistent_groups( [ $this->group ] ); + } + + /** + * Gets settings from the cache or database. + * + * @return array + */ + public function get_cache_settings(): array { + + $value = wp_cache_get( $this->option, $this->group ); + + if ( $value === false ) { + $value = (array) get_option( $this->option, [] ); + wp_cache_set( $this->option, $value, $this->group ); + } + + return $value; + } + + /** + * Gets a setting value from the cache or database. + * + * @param string $name The name of a bool setting. + * @param string $post_type The post type slug. + * @param bool $default_value The default value to return if the setting is not found. + * + * @return bool + */ + public function get_bool( string $name, string $post_type, bool $default_value = false ): bool { + $value = $this->get_cache_settings(); + + if ( ! $this->is_setting_of_type( $name, 'bool' ) || empty( $value[ $post_type ][ $name ] ) ) { + return $default_value; + } + + return (bool) $value[ $post_type ][ $name ]; + } + + /** + * Gets a setting value from the cache or database. + * + * @param string $name The name of a string setting. + * @param string $post_type The post type slug. + * @param string $default_value The default value to return if the setting is not found. + * + * @return string + */ + public function get_string( string $name, string $post_type, string $default_value = '' ): string { + $value = $this->get_cache_settings(); + + if ( ! $this->is_setting_of_type( $name, 'string' ) || empty( $value[ $post_type ][ $name ] ) ) { + return $default_value; + } + + return (string) $value[ $post_type ][ $name ]; + } + + /** + * Verifies if a setting allowed in the settings config and compares the type is correct. + * + * @param string $name The name of a setting. + * @param string $type The type of the setting. + * + * @return mixed + */ + private function is_setting_of_type( string $name, string $type ): bool { + return array_key_exists( $name, $this->settings_config ) && $this->settings_config[ $name ] === $type; + } + +} diff --git a/plugins/hwp-previews/src/Settings/Settings_Section.php b/plugins/hwp-previews/src/Settings/Settings_Section.php new file mode 100644 index 0000000..e953f82 --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Settings_Section.php @@ -0,0 +1,79 @@ + + */ + private array $fields; + + /** + * Constructor. + * + * @param string $id Page slug. + * @param string $title Settings section title. + * @param string $page The slug of the settings page. + * @param array<\HWP\Previews\Settings\Fields\Abstract_Settings_Field> $fields Array of fields to be registered in the section. + */ + public function __construct( + string $id, + string $title, + string $page, + array $fields + ) { + $this->id = $id; + $this->title = $title; + $this->page = $page; + $this->fields = $fields; + } + + /** + * Register the settings section. + * + * @param string $settings_key The settings key. + * @param string $post_type The post type. + * @param string $page The page slug. + * + * @return void + */ + public function register_section( string $settings_key, string $post_type, string $page ): void { + add_settings_section( + $this->id, + $this->title, + static fn() => null, + $this->page + ); + + foreach ( $this->fields as $field ) { + $field->register_settings_field( $settings_key, $this->id, $post_type, $page ); + } + } + +} diff --git a/plugins/hwp-previews/src/Settings/Tabbed_Settings.php b/plugins/hwp-previews/src/Settings/Tabbed_Settings.php new file mode 100644 index 0000000..d80678a --- /dev/null +++ b/plugins/hwp-previews/src/Settings/Tabbed_Settings.php @@ -0,0 +1,135 @@ + + */ + private array $tabs; + + /** + * Array of sanitization options where keys area options and values are types. + * + * @var array + */ + private array $sanitization_options; + + /** + * Constructor. + * + * @param string $option_group Settings option group. + * @param string $option_name Settings option name. + * @param array $tabs Tabs array as items allowed in the settings. + * @param array $sanitization_options Array of sanitization options where keys are options and values are types. + */ + public function __construct( + string $option_group, + string $option_name, + array $tabs, + array $sanitization_options + ) { + $this->option_group = $option_group; + $this->option_name = $option_name; + $this->tabs = $tabs; + $this->sanitization_options = $sanitization_options; + } + + /** + * Register settings. + * + * @return void + */ + public function register_settings(): void { + register_setting( + $this->option_group, + $this->option_name, + [ + 'sanitize_callback' => [ $this, 'sanitize_settings' ], + 'type' => 'array', + 'default' => [], + ] + ); + } + + /** + * Sanitize and merge new settings per-tab, pruning unknown fields. + * + * @param array $new_input New settings input for the specific tab that comes from the form for the sanitization. + * + * @return array + */ + public function sanitize_settings( array $new_input ): array { + $old_input = (array) get_option( $this->option_name, [] ); + + // Remove redundant tabs. + $old_input = array_intersect_key( $old_input, array_flip( $this->tabs ) ); + + $tab = array_keys( $new_input ); + if ( ! isset( $tab[0] ) ) { + return $old_input; // Wrong settings structure. + } + + $tab_to_sanitize = (string) $tab[0]; + if ( ! is_array( $new_input[ $tab_to_sanitize ] ) ) { + return $old_input; // Wrong settings structure. + } + + // Sanitize the fields in the tab. + $sanitized_fields = []; + foreach ( $new_input[ $tab_to_sanitize ] as $key => $value ) { + if ( ! isset( $this->sanitization_options[ $key ] ) ) { + continue; + } + + $sanitized_fields[ $key ] = $this->sanitize_field( $key, $value ); + } + + // Merge the sanitized fields with the old input. + $old_input[ $tab_to_sanitize ] = $sanitized_fields; + + return $old_input; + } + + /** + * Sanitize a single field value by type. + * + * @param string $key Field key. + * @param mixed $value Raw value. + * + * @return bool|int|string + */ + private function sanitize_field( string $key, $value ): bool|int|string { + $type = $this->sanitization_options[ $key ]; + + switch ( $type ) { + case 'bool': + return ! empty( $value ); + case 'int': + return intval( $value ); + case 'text': + default: + return sanitize_text_field( (string) $value ); + } + } + +} diff --git a/plugins/hwp-previews/src/Shared/Abstract_Model.php b/plugins/hwp-previews/src/Shared/Abstract_Model.php new file mode 100644 index 0000000..a7920e5 --- /dev/null +++ b/plugins/hwp-previews/src/Shared/Abstract_Model.php @@ -0,0 +1,28 @@ +|object|null $value The value to set. + * + * @return void + * + * @throws \Exception When attempting to modify a readonly property. + */ + public function __set( string $name, $value ): void { + throw new Exception( 'Cannot modify readonly property: ' . esc_html( $name ) ); + } + +} diff --git a/plugins/hwp-previews/src/Shared/Helpers.php b/plugins/hwp-previews/src/Shared/Helpers.php new file mode 100644 index 0000000..7650bca --- /dev/null +++ b/plugins/hwp-previews/src/Shared/Helpers.php @@ -0,0 +1,22 @@ + label array. + * + * @return array + */ + public static function get_public_post_types(): array { + return wp_list_pluck( + get_post_types( [ 'public' => true ], 'objects' ), + 'label', + 'name' + ); + } + +} diff --git a/plugins/hwp-previews/templates/admin.php b/plugins/hwp-previews/templates/admin.php new file mode 100644 index 0000000..854073d --- /dev/null +++ b/plugins/hwp-previews/templates/admin.php @@ -0,0 +1,38 @@ + + +
+

+

+ +
+ +
+ + +
diff --git a/plugins/hwp-previews/templates/admin/settings-page-main.php b/plugins/hwp-previews/templates/admin/settings-page-main.php new file mode 100644 index 0000000..a20eee8 --- /dev/null +++ b/plugins/hwp-previews/templates/admin/settings-page-main.php @@ -0,0 +1,84 @@ + + +
+

+ +

Just more text here

+ +

+ + + +
+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+

+
+
+ $desc ) : ?> + + + + +
+
+
+
+
+
+
+
+ + +
diff --git a/plugins/hwp-previews/templates/admin/settings-page-testing.php b/plugins/hwp-previews/templates/admin/settings-page-testing.php new file mode 100644 index 0000000..d7fed13 --- /dev/null +++ b/plugins/hwp-previews/templates/admin/settings-page-testing.php @@ -0,0 +1,13 @@ + + +
+

+

+ +
diff --git a/plugins/hwp-previews/templates/hwp-preview.php b/plugins/hwp-previews/templates/hwp-preview.php new file mode 100644 index 0000000..7136513 --- /dev/null +++ b/plugins/hwp-previews/templates/hwp-preview.php @@ -0,0 +1,29 @@ + + +
+ +
+ + Date: Fri, 9 May 2025 14:19:25 +0200 Subject: [PATCH 02/15] Adds zip of the plugin for installation. --- plugins/hwp-previews.zip | Bin 0 -> 135514 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/hwp-previews.zip diff --git a/plugins/hwp-previews.zip b/plugins/hwp-previews.zip new file mode 100644 index 0000000000000000000000000000000000000000..3949147a631accde3b76eb68b05e11601e5e1f07 GIT binary patch literal 135514 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 Date: Mon, 12 May 2025 15:07:40 +0200 Subject: [PATCH 03/15] Final changes, cs fixes, readme update. --- plugins/hwp-previews.zip | Bin 135514 -> 139826 bytes plugins/hwp-previews/README.md | 139 +++++++ .../hwp-previews/assets/js/hwp-previews.js | 1 + plugins/hwp-previews/hwp-previews.php | 5 +- plugins/hwp-previews/phpcs.xml | 1 + plugins/hwp-previews/psalm.xml | 2 +- plugins/hwp-previews/src/Plugin.php | 368 ++++++++++++++---- .../Contracts/Post_Types_Config_Interface.php | 4 +- .../src/Post/Type/Post_Types_Config.php | 11 +- .../Preview_Link_Placeholder_Resolver.php | 2 +- ....php => Post_Types_Settings_Interface.php} | 4 +- .../Fields/Abstract_Settings_Field.php | 65 +++- .../src/Settings/Menu/Menu_Page.php | 4 +- .../src/Settings/Preview_Settings.php | 4 +- .../src/Settings/Settings_Cache_Group.php | 2 +- .../src/Settings/Settings_Section.php | 5 +- .../src/Settings/Tabbed_Settings.php | 6 +- plugins/hwp-previews/src/Shared/Helpers.php | 22 -- .../templates/admin/settings-page-main.php | 4 - 19 files changed, 515 insertions(+), 134 deletions(-) create mode 100644 plugins/hwp-previews/README.md create mode 100644 plugins/hwp-previews/assets/js/hwp-previews.js rename plugins/hwp-previews/src/Settings/Contracts/{CPT_Settings_Interface.php => Post_Types_Settings_Interface.php} (96%) delete mode 100644 plugins/hwp-previews/src/Shared/Helpers.php diff --git a/plugins/hwp-previews.zip b/plugins/hwp-previews.zip index 3949147a631accde3b76eb68b05e11601e5e1f07..e8a4b6aa9ef80d41ddfe76abd402338428224532 100644 GIT binary patch delta 27337 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 delta 23365 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 x_d5X(n*-pa4%L@e)EpW^G@unregister( 'author_ID' ); + + // Add custom parameter + $registry->register( new \HWP\Previews\Preview\Parameter\Preview_Parameter( + 'current_time', + static fn( \WP_Post $post ) => (string) time(), + __( 'Current Unix timestamp', 'your-domain' ) + ) ); +} +``` + +Modify post types and statuses: + +```php +add_action( 'hwp_previews_core', 'modify_post_types_and_statuses_configs', 10, 3 ); +function modify_post_types_and_statuses_configs( + \HWP\Previews\Preview\Parameter\Preview_Parameter_Registry $registry, + \HWP\Previews\Post\Type\Post_Types_Config $types, + \HWP\Previews\Post\Status\Post_Statuses_Config $statuses +) { + // Limit to pages only + $types->set_post_types( [ 'page' ] ); + // Only include drafts + $statuses->set_post_statuses( [ 'draft' ] ); +} +``` + +### Filter: Iframe Template Path + +Use your own template for iframe previews: + +```php +add_filter( 'hwp_previews_template_path', function( $default_path ) { + return get_stylesheet_directory() . '/my-preview-template.php'; +}); +``` + +--- + +## Integration + + + +--- diff --git a/plugins/hwp-previews/assets/js/hwp-previews.js b/plugins/hwp-previews/assets/js/hwp-previews.js new file mode 100644 index 0000000..b6735e8 --- /dev/null +++ b/plugins/hwp-previews/assets/js/hwp-previews.js @@ -0,0 +1 @@ +console.log('hwp-preview-test'); diff --git a/plugins/hwp-previews/hwp-previews.php b/plugins/hwp-previews/hwp-previews.php index cdbcf30..cc3dd05 100644 --- a/plugins/hwp-previews/hwp-previews.php +++ b/plugins/hwp-previews/hwp-previews.php @@ -25,7 +25,8 @@ define( 'HWP_PREVIEWS_BASENAME', plugin_basename( __FILE__ ) ); -add_action( 'plugins_loaded', static fn() => HWP\Previews\Plugin::get_instance()->init( +add_action( 'plugins_loaded', static fn() => HWP\Previews\Plugin::get_instance( '0.0.1', plugin_dir_path( __FILE__ ), -), 5, 0 ); + plugin_dir_url( __FILE__ ) +)->init(), 5, 0 ); diff --git a/plugins/hwp-previews/phpcs.xml b/plugins/hwp-previews/phpcs.xml index c9d99d3..bb723b9 100644 --- a/plugins/hwp-previews/phpcs.xml +++ b/plugins/hwp-previews/phpcs.xml @@ -199,6 +199,7 @@ + diff --git a/plugins/hwp-previews/psalm.xml b/plugins/hwp-previews/psalm.xml index 1d8acdc..1fc1e56 100644 --- a/plugins/hwp-previews/psalm.xml +++ b/plugins/hwp-previews/psalm.xml @@ -1,6 +1,6 @@ */ public const SETTINGS_FIELDS = [ @@ -90,34 +129,100 @@ class Plugin { ]; /** + * Settings object used for value retrieving. + * * @var \HWP\Previews\Settings\Preview_Settings */ private Preview_Settings $settings; + /** + * Post types configuration. + * + * @var \HWP\Previews\Post\Type\Contracts\Post_Types_Config_Interface + */ private Post_Types_Config_Interface $types_config; + + /** + * Post statuses configuration. + * + * @var \HWP\Previews\Post\Status\Contracts\Post_Statuses_Config_Interface + */ private Post_Statuses_Config_Interface $statuses_config; + + /** + * Preview parameter registry. + * + * @var \HWP\Previews\Preview\Parameter\Preview_Parameter_Registry + */ private Preview_Parameter_Registry $parameters; /** + * Preview link service class that handles the generation of preview links. + * * @var \HWP\Previews\Preview\Link\Preview_Link_Service */ private Preview_Link_Service $link_service; - private string $version; // Todo: use when enqueuing scripts. + + /** + * The version of the plugin. + * + * @var string + */ + private string $version; + + /** + * The directory path of the plugin. + * + * @var string + */ private string $dir_path; + + /** + * The URL of the plugin. + * + * @var string + */ + private string $plugin_url; + + /** + * The instance of the plugin. + * + * @var \HWP\Previews\Plugin|null + */ private static ?Plugin $instance = null; /** * Constructor. + * + * @param string $version The version of the plugin. + * @param string $dir_path The directory path of the plugin. + * @param string $plugin_url The URL of the plugin. */ - private function __construct() { + private function __construct( string $version, string $dir_path, string $plugin_url ) { + $this->version = $version; + $this->dir_path = $dir_path; + $this->plugin_url = $plugin_url; + + // Initialize the settings object with a cache group. $this->settings = new Preview_Settings( new Settings_Cache_Group( self::SETTINGS_KEY, self::SETTINGS_GROUP, self::SETTINGS_FIELDS ) ); - $this->types_config = new Post_Types_Config(); - $this->statuses_config = new Post_Statuses_Config(); - $this->parameters = new Preview_Parameter_Registry(); - + // Initialize the post types and statuses configurations. + $this->types_config = ( new Post_Types_Config() )->set_post_types( $this->settings->post_types_enabled() ); + $this->statuses_config = ( new Post_Statuses_Config() )->set_post_statuses( [ + 'publish', + 'future', + 'draft', + 'pending', + 'private', + 'auto-draft' + ] ); + + // Initialize the preview parameter registry. + $this->parameters = new Preview_Parameter_Registry(); + + // Initialize the preview link service. $this->link_service = new Preview_Link_Service( $this->types_config, $this->statuses_config, @@ -126,20 +231,26 @@ private function __construct() { } /** - * Get the instance of this class. + * Get the instance of this class. Passes the version, directory path, and plugin URL to the constructor. + * + * @param string $version The version of the plugin. + * @param string $dir_path The directory path of the plugin. + * @param string $plugin_url The URL of the plugin. + * + * @return \HWP\Previews\Plugin */ - public static function get_instance(): Plugin { + public static function get_instance( string $version, string $dir_path, string $plugin_url ): Plugin { if ( self::$instance === null ) { - self::$instance = new self(); + self::$instance = new self( $version, $dir_path, $plugin_url ); } return self::$instance; } - public function init( string $version, string $dir_path ): void { - $this->version = $version; - $this->dir_path = $dir_path; - + /** + * Initialize the plugin functionality. + */ + public function init(): void { // Init core functionality. $this->init_core_functionality(); @@ -147,6 +258,9 @@ public function init( string $version, string $dir_path ): void { $this->register_settings_pages(); $this->register_settings_fields(); + // JS. + $this->enqueue_plugin_js(); + // Functionality. $this->enable_unique_post_slug(); $this->enable_post_statuses_as_parent(); @@ -154,6 +268,32 @@ public function init( string $version, string $dir_path ): void { $this->enable_preview_functionality(); } + /** + * Enqueues the JavaScript file for the plugin admin area. + * + * @return void + */ + public function enqueue_plugin_js(): void { + add_action( 'admin_enqueue_scripts', function ( string $hook ) { + if ( $hook !== 'toplevel_page_' . self::PLUGIN_MENU_SLUG ) { + return; + } + + wp_enqueue_script( + self::PLUGIN_JS_HANDLE, + trailingslashit( $this->plugin_url ) . self::PLUGIN_JS_SRC, + [], + $this->version, + true + ); + } ); + } + + /** + * Enable unique post slugs for post statuses specified in the post statuses config. + * + * @return void + */ public function enable_unique_post_slug(): void { $post_slug_manger = new Post_Slug_Manager( $this->types_config, @@ -179,26 +319,45 @@ public function enable_unique_post_slug(): void { }, 10, 2 ); } + /** + * Replace the preview link in the REST response. + * Todo: check when new draft created. + * + * @param \WP_REST_Response $response The REST response object. + * @param \WP_Post $post The post object. + * + * @return \WP_REST_Response + */ + public function filter_rest_prepare_link( WP_REST_Response $response, WP_Post $post ): WP_REST_Response { + if ( $this->settings->in_iframe( $post->post_type ) ) { + return $response; + } + + $preview_url = $this->generate_preview_url( $post ); + if ( ! empty( $preview_url ) ) { + $response->data['link'] = $preview_url; + } + + return $response; + } + + /** + * Setups default preview parameters on the 'init' hook. + * Creates custom action hook 'hwp_previews_core'. + * + * @return void + */ private function init_core_functionality(): void { add_action( 'init', function (): void { // Register default preview parameters. $this->setup_default_preview_parameters(); - // Setup post types configuration. - $this->types_config->set_post_types( - $this->settings->post_types_enabled() - ); - - // Setup post statuses configuration. - $this->statuses_config->set_post_statuses( - [ 'publish', 'future', 'draft', 'pending', 'private' ] - ); /** - * Allows modify parameters registry. + * Allows access to the parameters registry, types config, statuses config. */ - do_action( 'hwp_previews_parameters_registry', $this->parameters ); + do_action( 'hwp_previews_core', $this->parameters, $this->types_config, $this->statuses_config ); }, 5, 0 ); } @@ -229,25 +388,39 @@ private function setup_default_preview_parameters(): void { ); } + /** + * Registers settings pages and subpages. + * + * @return void + */ private function register_settings_pages(): void { add_action( 'admin_menu', function (): void { /** - * @var array $post_types Array of post types where key is the post type slug and value is the label. + * Array of post types where key is the post type slug and value is the label. + * + * @var array $post_types */ - $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', Helpers::get_public_post_types() ); + $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', $this->types_config->get_public_post_types() ); $this->create_settings_page( $post_types )->register_page(); $this->create_settings_subpage()->register_page(); } ); } + /** + * Registers settings fields. + * + * @return void + */ private function register_settings_fields(): void { add_action( 'admin_init', function (): void { /** - * @var array $post_types Array of post types where key is the post type slug and value is the label. + * Array of post types where key is the post type slug and value is the label. + * + * @var array $post_types */ - $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', Helpers::get_public_post_types() ); + $post_types = apply_filters( 'hwp_previews_filter_post_type_setting', $this->types_config->get_public_post_types() ); /** * Register setting itself. @@ -260,9 +433,14 @@ private function register_settings_fields(): void { foreach ( $post_types as $post_type => $label ) { $this->create_setting_section( $post_type, $label )->register_section( self::SETTINGS_KEY, $post_type, "hwp-previews-{$post_type}" ); } - } ); + }, 10, 0 ); } + /** + * Enable post statuses specified in the post statuses config as parent for the post types specified in the post types config. + * + * @return void + */ private function enable_post_statuses_as_parent(): void { $post_parent_manager = new Post_Parent_Manager( $this->types_config, $this->statuses_config ); @@ -297,6 +475,11 @@ private function enable_post_statuses_as_parent(): void { } } + /** + * Enable preview functionality in iframe. + * + * @return void + */ private function enable_preview_in_iframe(): void { $template_resolver = new Preview_Template_Resolver( $this->types_config, $this->statuses_config ); @@ -316,10 +499,10 @@ private function enable_preview_in_iframe(): void { } /** - * The filter 'hwp_previews_template_dir_path' allows to change the template directory path. + * The filter 'hwp_previews_template_path' allows to change the template directory path. */ $template_dir_path = (string) apply_filters( - 'hwp_previews_template_dir_path', + 'hwp_previews_template_path', $this->dir_path . 'templates/hwp-preview.php' ); @@ -335,6 +518,12 @@ private function enable_preview_in_iframe(): void { }, 999 ); } + /** + * Swaps the preview link for the post types specified in the post types config. + * Is being enabled only if the preview is not in iframe. Otherwise preview functionality is resolved on the template redirect level. + * + * @return void + */ private function enable_preview_functionality(): void { add_filter( 'preview_post_link', function ( $link, $post ) { // If iframe option is enabled, we need to resolve preview on the template redirect level. @@ -342,42 +531,45 @@ private function enable_preview_functionality(): void { return $link; } - return $this->generate_preview_url( $post ) ?: $link; + $url = $this->generate_preview_url( $post ); + + return ! empty( $url ) ? $url : $link; }, 10, 2 ); /** * Hack Function that changes the preview link for draft articles, * this must be removed when properly fixed https://github.com/WordPress/gutenberg/issues/13998. */ - foreach ( $this->types_config->get_post_types() as $post_type ) { - add_filter( 'rest_prepare_' . $post_type, function ( $response, $post ) { - // If iframe option is enabled, we need to resolve preview on the template redirect level. - if ( $this->settings->in_iframe( $post->post_type ) ) { - return $response; - } - - $preview_url = $this->generate_preview_url( $post ); - if ( $preview_url ) { - $response->data['link'] = $preview_url; - } - - return $response; - }, 10, 2 ); + foreach ( $this->types_config->get_public_post_types() as $key => $label ) { + add_filter( 'rest_prepare_' . $key, [ $this, 'filter_rest_prepare_link' ], 10, 2 ); } } + /** + * Generates the preview URL for the given post based on the preview URL template provided in settings. + * + * @param \WP_Post $post The post object. + * + * @return string The generated preview URL. + */ private function generate_preview_url( WP_Post $post ): string { $url = $this->settings->url_template( $post->post_type ); - if ( ! empty( $url ) ) { - return $this->link_service->generate_preview_post_link( $url, $post ); + if ( empty( $url ) ) { + return ''; } - return ''; + return (new Preview_Link_Service( + $this->types_config, + $this->statuses_config, + new Preview_Link_Placeholder_Resolver( $this->parameters ) + ))->generate_preview_post_link( $url, $post ); } /** - * @param array $post_types + * Creates the settings page. + * + * @param array $post_types The post types to be used in the settings page. * * @return \HWP\Previews\Settings\Menu\Menu_Page */ @@ -385,12 +577,12 @@ private function create_settings_page( array $post_types ): Menu_Page { return new Menu_Page( __( 'HWP Previews Settings', 'hwp-previews' ), 'HWP Previews', - 'hwp-previews', + self::PLUGIN_MENU_SLUG, $this->dir_path . 'templates/admin/settings-page-main.php', [ self::SETTINGS_ARGS => [ 'tabs' => $post_types, - 'current_tab' => isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : key( $post_types ), + 'current_tab' => $this->get_current_tab( $post_types ), 'params' => $this->parameters->get_descriptions(), ], ], @@ -398,9 +590,31 @@ private function create_settings_page( array $post_types ): Menu_Page { ); } + /** + * Get the current tab for the settings page. + * + * @param array $post_types The post types to be used in the settings page. + * @param string $tab The name of the tab. + * + * @return string + */ + private function get_current_tab( $post_types, string $tab = 'tab' ): string { + // phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET[ $tab ] ) && is_string( $_GET[ $tab ] ) ) { + return sanitize_key( $_GET[ $tab ] ); + } + + return ! empty( $post_types ) ? (string) key( $post_types ) : ''; + } + + /** + * Creates the settings subpage. + * + * @return \HWP\Previews\Settings\Menu\Submenu_Page + */ private function create_settings_subpage(): Submenu_Page { return new Submenu_Page( - 'hwp-previews', + self::PLUGIN_MENU_SLUG, __( 'Testing Tool', 'hwp-previews' ), 'Testing Tool', 'hwp-previews-testing-tool', @@ -409,7 +623,9 @@ private function create_settings_subpage(): Submenu_Page { } /** - * @param array $post_types + * Creates the tabbed settings object. + * + * @param array $post_types Post Types as a tabs. * * @return \HWP\Previews\Settings\Tabbed_Settings */ @@ -417,11 +633,19 @@ private function create_tabbed_settings( array $post_types ): Tabbed_Settings { return new Tabbed_Settings( self::SETTINGS_GROUP, self::SETTINGS_KEY, - array_keys( $post_types ), // Items allowed, represented as a tabs. + array_keys( $post_types ), self::SETTINGS_FIELDS ); } + /** + * Creates the settings section for a specific post type. + * + * @param string $post_type The post type slug. + * @param string $label The label for the post type. + * + * @return \HWP\Previews\Settings\Settings_Section + */ private function create_setting_section( string $post_type, string $label ): Settings_Section { return new Settings_Section( 'hwp_previews_section_' . $post_type, @@ -432,34 +656,32 @@ private function create_setting_section( string $post_type, string $label ): Set } /** - * @param string $post_type - * @param string $label - * @param bool $is_hierarchical + * Creates the settings fields for a specific post type. + * + * @param string $post_type The post type slug. + * @param string $label The label for the post type. + * @param bool $is_hierarchical Whether the post type is hierarchical. * * @return array<\HWP\Previews\Settings\Fields\Abstract_Settings_Field> */ private function create_settings_fields( string $post_type, string $label, bool $is_hierarchical ): array { $fields = []; - foreach ( - [ - 'enabled' => sprintf( __( 'Enable HWP Previews for %s', 'hwp-previews' ), $label ), - 'unique_post_slugs' => __( 'Enable unique post slugs for all post statuses', 'hwp-previews' ), - 'post_statuses_as_parent' => __( 'Allow all post statuses in parents option', 'hwp-previews' ), - 'in_iframe' => sprintf( __( 'Load previews in iframe', 'hwp-previews' ), $label ), - ] as $id => $description - ) { - $fields[ $id ] = new Checkbox_Field( $id, $description ); - } + $fields[] = new Checkbox_Field( + 'enabled', + // translators: %s is the label of the post type. + sprintf( __( 'Enable HWP Previews for %s', 'hwp-previews' ), $label ) + ); + $fields[] = new Checkbox_Field( 'unique_post_slugs', __( 'Enable unique post slugs for all post statuses', 'hwp-previews' ) ); - // Remove the 'post_statuses_as_parent' field if the post type is not hierarchical. - if ( ! $is_hierarchical ) { - unset( $fields['post_statuses_as_parent'] ); + if ( $is_hierarchical ) { + $fields[] = new Checkbox_Field( 'post_statuses_as_parent', __( 'Allow all post statuses in parents option', 'hwp-previews' ) ); } - // Preview URL field. + $fields[] = new Checkbox_Field( 'in_iframe', sprintf( __( 'Load previews in iframe', 'hwp-previews' ), $label ) ); $fields[] = new Text_Input_Field( 'preview_url', + // translators: %s is the label of the post type. sprintf( __( 'Preview URL for %s', 'hwp-previews' ), $label ), "https://example.com/{$post_type}?preview=true&post_id={ID}&name={slug}", 'large-text code hwp-previews-url' // The class is being used as a query for the JS. diff --git a/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php b/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php index 4585d2e..721e781 100644 --- a/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php +++ b/plugins/hwp-previews/src/Post/Type/Contracts/Post_Types_Config_Interface.php @@ -9,7 +9,7 @@ interface Post_Types_Config_Interface { /** - * Get the post types that are applicable for previews. + * Set the post types that are applicable for previews. * * @param array $post_types The post type to check. */ @@ -48,6 +48,6 @@ public function supports_gutenberg( WP_Post_Type $post_type ): bool; * * @return array */ - public function get_publicly_available_post_types(): array; + public function get_public_post_types(): array; } diff --git a/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php b/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php index dba967d..40a2ade 100644 --- a/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php +++ b/plugins/hwp-previews/src/Post/Type/Post_Types_Config.php @@ -98,10 +98,15 @@ public function supports_gutenberg( WP_Post_Type $post_type ): bool { * * @return array */ - public function get_publicly_available_post_types(): array { - $post_type_objects = get_post_types( [ 'public' => true ], 'objects' ); + public function get_public_post_types(): array { + $post_types = get_post_types( [ 'public' => true ], 'objects' ); + $result = []; - return wp_list_pluck( $post_type_objects, 'label', 'name' ); + foreach ( $post_types as $post_type ) { + $result[ $post_type->name ] = $post_type->label; + } + + return $result; } } diff --git a/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php index bd75133..0cc1c8e 100644 --- a/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php +++ b/plugins/hwp-previews/src/Preview/Link/Preview_Link_Placeholder_Resolver.php @@ -49,7 +49,7 @@ public function __construct(Preview_Parameter_Registry $registry) { * @return string */ public function resolve_placeholders(string $template, WP_Post $post ): string { - return preg_replace_callback( + return (string) preg_replace_callback( self::PLACEHOLDER_REGEX, fn(array $matches): string => rawurlencode( $this->resolve_token( $matches[1], $post ) ), $template diff --git a/plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php b/plugins/hwp-previews/src/Settings/Contracts/Post_Types_Settings_Interface.php similarity index 96% rename from plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php rename to plugins/hwp-previews/src/Settings/Contracts/Post_Types_Settings_Interface.php index b3858e3..8bc1077 100644 --- a/plugins/hwp-previews/src/Settings/Contracts/CPT_Settings_Interface.php +++ b/plugins/hwp-previews/src/Settings/Contracts/Post_Types_Settings_Interface.php @@ -4,7 +4,7 @@ namespace HWP\Previews\Settings\Contracts; -interface CPT_Settings_Interface { +interface Post_Types_Settings_Interface { /** * Gets all enabled for the preview functionality post types. @@ -21,7 +21,7 @@ public function post_types_enabled( array $default_value = [] ): array; * @param string $post_type Post type slug. * @param string $default_value Default URL template. * - * @return string + * @return string */ public function url_template( string $post_type, string $default_value = '' ): string; diff --git a/plugins/hwp-previews/src/Settings/Fields/Abstract_Settings_Field.php b/plugins/hwp-previews/src/Settings/Fields/Abstract_Settings_Field.php index a539dd2..9ddc07d 100644 --- a/plugins/hwp-previews/src/Settings/Fields/Abstract_Settings_Field.php +++ b/plugins/hwp-previews/src/Settings/Fields/Abstract_Settings_Field.php @@ -27,6 +27,20 @@ abstract class Abstract_Settings_Field { */ private string $title; + /** + * The settings field settings key. + * + * @var string + */ + private string $settings_key; + + /** + * The settings field post type. + * + * @var string + */ + private string $post_type; + /** * Render the settings field. * @@ -56,42 +70,61 @@ public function __construct( } /** - * Register the settings field. + * Set the settings key. * * @param string $settings_key The settings key. - * @param string $section The settings section. + * + * @return $this + */ + public function set_settings_key( string $settings_key ): self { + $this->settings_key = $settings_key; + + return $this; + } + + /** + * Set the post type. + * * @param string $post_type The post type. + * + * @return $this + */ + public function set_post_type( string $post_type ): self { + $this->post_type = $post_type; + + return $this; + } + + /** + * Register the settings field. + * + * @param string $section The settings section. * @param string $page The settings page. * * @return void */ - public function register_settings_field( string $settings_key, string $section, string $post_type, string $page ): void { + public function register_settings_field( string $section, string $page ): void { + add_settings_field( $this->id, $this->title, [ $this, 'settings_field_callback' ], $page, - $section, - [ - 'settings_key' => $settings_key, - 'post_type' => $post_type, - ] + $section ); } /** * Callback for the settings field. * - * @param array $args The settings field arguments. - * * @return void */ - public function settings_field_callback( array $args ): void { - $settings_key = (string) ( $args['settings_key'] ?? '' ); - $post_type = (string) ( $args['post_type'] ?? '' ); - $value = $this->get_setting_value( $settings_key, $post_type ); - - $this->render_field( $value, $settings_key, $post_type ); + public function settings_field_callback(): void { + $this->render_field( + $this->get_setting_value( $this->settings_key, $this->post_type ), + $this->settings_key, + $this->post_type + ); } /** diff --git a/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php b/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php index b7b1728..427cc91 100644 --- a/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php +++ b/plugins/hwp-previews/src/Settings/Menu/Menu_Page.php @@ -102,12 +102,14 @@ public function registration_callback(): void { if ( empty( $this->template ) || ! file_exists( $this->template ) ) { printf( '

%s

', - __( 'The HWP Previews Settings template does not exist.', 'hwp-previews' ) + esc_html__( 'The HWP Previews Settings template does not exist.', 'hwp-previews' ) ); return; } $this->set_query_vars(); + + // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable -- $this->template is validated and defined within the class include_once $this->template; } diff --git a/plugins/hwp-previews/src/Settings/Preview_Settings.php b/plugins/hwp-previews/src/Settings/Preview_Settings.php index 5d4d841..8291102 100644 --- a/plugins/hwp-previews/src/Settings/Preview_Settings.php +++ b/plugins/hwp-previews/src/Settings/Preview_Settings.php @@ -5,9 +5,9 @@ namespace HWP\Previews\Settings; use HWP\Previews\Plugin; -use HWP\Previews\Settings\Contracts\CPT_Settings_Interface; +use HWP\Previews\Settings\Contracts\Post_Types_Settings_Interface; -class Preview_Settings implements CPT_Settings_Interface { +class Preview_Settings implements Post_Types_Settings_Interface { /** * The settings cache group. diff --git a/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php b/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php index fe961fd..dc3c524 100644 --- a/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php +++ b/plugins/hwp-previews/src/Settings/Settings_Cache_Group.php @@ -104,7 +104,7 @@ public function get_string( string $name, string $post_type, string $default_val * @param string $name The name of a setting. * @param string $type The type of the setting. * - * @return mixed + * @return bool */ private function is_setting_of_type( string $name, string $type ): bool { return array_key_exists( $name, $this->settings_config ) && $this->settings_config[ $name ] === $type; diff --git a/plugins/hwp-previews/src/Settings/Settings_Section.php b/plugins/hwp-previews/src/Settings/Settings_Section.php index e953f82..a02957f 100644 --- a/plugins/hwp-previews/src/Settings/Settings_Section.php +++ b/plugins/hwp-previews/src/Settings/Settings_Section.php @@ -72,7 +72,10 @@ public function register_section( string $settings_key, string $post_type, strin ); foreach ( $this->fields as $field ) { - $field->register_settings_field( $settings_key, $this->id, $post_type, $page ); + $field->set_settings_key( $settings_key ); + $field->set_post_type( $post_type ); + + $field->register_settings_field( $this->id, $page ); } } diff --git a/plugins/hwp-previews/src/Settings/Tabbed_Settings.php b/plugins/hwp-previews/src/Settings/Tabbed_Settings.php index d80678a..f916667 100644 --- a/plugins/hwp-previews/src/Settings/Tabbed_Settings.php +++ b/plugins/hwp-previews/src/Settings/Tabbed_Settings.php @@ -97,11 +97,11 @@ public function sanitize_settings( array $new_input ): array { // Sanitize the fields in the tab. $sanitized_fields = []; foreach ( $new_input[ $tab_to_sanitize ] as $key => $value ) { - if ( ! isset( $this->sanitization_options[ $key ] ) ) { + if ( ! isset( $this->sanitization_options[ (string) $key ] ) ) { continue; } - $sanitized_fields[ $key ] = $this->sanitize_field( $key, $value ); + $sanitized_fields[ $key ] = $this->sanitize_field( (string) $key, $value ); } // Merge the sanitized fields with the old input. @@ -118,7 +118,7 @@ public function sanitize_settings( array $new_input ): array { * * @return bool|int|string */ - private function sanitize_field( string $key, $value ): bool|int|string { + private function sanitize_field( string $key, $value ) { $type = $this->sanitization_options[ $key ]; switch ( $type ) { diff --git a/plugins/hwp-previews/src/Shared/Helpers.php b/plugins/hwp-previews/src/Shared/Helpers.php deleted file mode 100644 index 7650bca..0000000 --- a/plugins/hwp-previews/src/Shared/Helpers.php +++ /dev/null @@ -1,22 +0,0 @@ - label array. - * - * @return array - */ - public static function get_public_post_types(): array { - return wp_list_pluck( - get_post_types( [ 'public' => true ], 'objects' ), - 'label', - 'name' - ); - } - -} diff --git a/plugins/hwp-previews/templates/admin/settings-page-main.php b/plugins/hwp-previews/templates/admin/settings-page-main.php index a20eee8..53cfa09 100644 --- a/plugins/hwp-previews/templates/admin/settings-page-main.php +++ b/plugins/hwp-previews/templates/admin/settings-page-main.php @@ -12,10 +12,6 @@

-

Just more text here

- -

-