diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 48e486cf..f2c434af 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -43,7 +43,32 @@ jobs: exit 0 fi - PLUGIN="${{ steps.plugin.outputs.slug }}" + # Extract unique plugin names and check for phpcs.xml + PLUGINS_WITH_CONFIG=() + CHECKED_PLUGINS=() + + for file in $CHANGED_FILES; do + # Skip if this is not a file within a plugin subdirectory + if [[ ! "$file" =~ ^plugins/[^/]+/.+ ]]; then + continue + fi + + plugin=$(echo $file | cut -d/ -f2) + + # Skip if we already checked this plugin + if [[ " ${CHECKED_PLUGINS[@]} " =~ " $plugin " ]]; then + continue + fi + + CHECKED_PLUGINS+=("$plugin") + + if [ -f "plugins/$plugin/phpcs.xml" ]; then + PLUGINS_WITH_CONFIG+=("$plugin") + echo "✅ Found phpcs.xml for plugin: $plugin" + else + echo "ℹ️ No phpcs.xml found for plugin: $plugin, skipping quality checks" + fi + done if [ -f "plugins/$PLUGIN/phpcs.xml" ]; then echo "plugins=[\"$PLUGIN\"]" >> $GITHUB_OUTPUT diff --git a/.github/workflows/codeception.yml b/.github/workflows/codeception.yml index c91c0a3f..81c4ce68 100644 --- a/.github/workflows/codeception.yml +++ b/.github/workflows/codeception.yml @@ -50,7 +50,39 @@ jobs: exit 0 fi - PLUGIN="${{ steps.plugin.outputs.slug }}" + # Extract unique plugin names and check for test config + PLUGINS_WITH_CONFIG=() + CHECKED_PLUGINS=() + + for file in $CHANGED_FILES; do + # Skip if this is not a file within a plugin subdirectory + if [[ ! "$file" =~ ^plugins/[^/]+/.+ ]]; then + continue + fi + + plugin=$(echo $file | cut -d/ -f2) + + # Skip if we already checked this plugin + if [[ " ${CHECKED_PLUGINS[@]} " =~ " $plugin " ]]; then + continue + fi + + CHECKED_PLUGINS+=("$plugin") + + # Check for both codeception.dist.yml and composer.json + if [ -f "plugins/$plugin/codeception.dist.yml" ] && [ -f "plugins/$plugin/composer.json" ]; then + PLUGINS_WITH_CONFIG+=("$plugin") + echo "✅ Found test config for plugin: $plugin (codeception.dist.yml + composer.json)" + else + echo "ℹ️ Missing test config for plugin: $plugin, skipping tests" + if [ ! -f "plugins/$plugin/codeception.dist.yml" ]; then + echo " - Missing: codeception.dist.yml" + fi + if [ ! -f "plugins/$plugin/composer.json" ]; then + echo " - Missing: composer.json" + fi + fi + done if [ -f "plugins/$PLUGIN/codeception.dist.yml" ]; then echo "plugins=[\"$PLUGIN\"]" >> $GITHUB_OUTPUT diff --git a/.github/workflows/plugin-artifact-for-pr.yml b/.github/workflows/plugin-artifact-for-pr.yml index f22025bb..6806c828 100644 --- a/.github/workflows/plugin-artifact-for-pr.yml +++ b/.github/workflows/plugin-artifact-for-pr.yml @@ -20,7 +20,42 @@ jobs: - name: Get changed plugin directory id: plugin run: | - bash .github/scripts/get-plugin-slug.sh ${{ github.event.pull_request.base.sha }} ${{ github.sha }} + git fetch --prune --unshallow + + # Get changed files in plugins subdirectories + CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep '^plugins/[^/]\+/' || true) + + if [ -z "$CHANGED_FILES" ]; then + echo "No plugin files changed" + exit 1 + fi + + # Extract plugin names from both old and new paths + PLUGINS=() + for file in $CHANGED_FILES; do + plugin=$(echo $file | cut -d/ -f2) + PLUGINS+=("$plugin") + done + + # Get unique plugin names + UNIQUE_PLUGINS=($(printf '%s\n' "${PLUGINS[@]}" | sort -u)) + + # Find the first plugin that actually exists + PLUGIN_SLUG="" + for plugin in "${UNIQUE_PLUGINS[@]}"; do + if [ -d "plugins/$plugin" ]; then + PLUGIN_SLUG="$plugin" + echo "Found existing plugin directory: $PLUGIN_SLUG" + break + fi + done + + if [ -z "$PLUGIN_SLUG" ]; then + echo "No valid plugin directory found" + exit 1 + fi + + echo "slug=$PLUGIN_SLUG" >> $GITHUB_OUTPUT - name: Create plugin artifact uses: ./.github/actions/create-plugin-artifact diff --git a/examples/next/webhooks-isr/.wp-env.json b/examples/next/webhooks-isr/.wp-env.json index 19c93194..504e27c9 100644 --- a/examples/next/webhooks-isr/.wp-env.json +++ b/examples/next/webhooks-isr/.wp-env.json @@ -4,7 +4,7 @@ "https://github.com/wp-graphql/wp-graphql/releases/latest/download/wp-graphql.zip", "https://github.com/wp-graphql/wp-graphql-smart-cache/releases/download/v2.0.0/wpgraphql-smart-cache.zip", "https://downloads.wordpress.org/plugin/code-snippets.3.6.8.zip", - "../../../plugins/wp-graphql-webhooks" + "../../../plugins/wpgraphql-webhooks" ], "config": { "WP_DEBUG": true, diff --git a/plugins/README.md b/plugins/README.md index abdfa5a0..a46e34e4 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -7,8 +7,7 @@ WordPress plugins for the Headless WordPress Toolkit. Each plugin is paired with | Plugin | Description | |--------|-------------| | [`hwp-previews`](./hwp-previews/README.md) | Headless Previews solution for WordPress: fully configurable preview URLs via the settings page which is framework agnostic. | -| [`wp-graphql-webhooks`](./wp-graphql-webhooks/README.md) | A WordPress plugin that extends [WPGraphQL](https://www.wpgraphql.com/) to support webhook subscriptions and dispatching for headless WordPress environments. This allows external applications to listen and respond to WordPress content changes through GraphQL-driven webhook events. | - +| [`wpgraphql-webhooks`](./wpgraphql-webhooks/README.md) | A WordPress plugin that extends [WPGraphQL](https://www.wpgraphql.com/) to support webhook subscriptions and dispatching for headless WordPress environments. This allows external applications to listen and respond to WordPress content changes through GraphQL-driven webhook events. | ## Contributing diff --git a/plugins/wpgraphql-webhooks/.distignore b/plugins/wpgraphql-webhooks/.distignore new file mode 100644 index 00000000..24deaa54 --- /dev/null +++ b/plugins/wpgraphql-webhooks/.distignore @@ -0,0 +1,36 @@ +/.devcontainer +/.git +/.github +/.idea +/.log +/.vscode +/.wordpress-org +/bin +/docker +/docker-output +/docs +/img +/phpstan +/plugin-build +/tests + +.coveralls.yml +.distignore +.dockerignore +.DS_Store +.env +.env.dist +.gitattributes +.gitignore +.phpcs.xml +.phpcs.xml.dist +.travis.yml +CHANGELOG.md +codeception.dist.yml +codeception.yml +composer.json +composer.lock +docker-compose.yml +phpstan.neon.dist +README.md +schema.graphql \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/.editorconfig b/plugins/wpgraphql-webhooks/.editorconfig new file mode 100644 index 00000000..d7dbce73 --- /dev/null +++ b/plugins/wpgraphql-webhooks/.editorconfig @@ -0,0 +1,27 @@ +# 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 +indent_size = 4 +tab_width = 4 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.txt] +trim_trailing_whitespace = false + +[*.{md,json,yml}] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 + +[*.json] +indent_style = tab \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/.env.dist b/plugins/wpgraphql-webhooks/.env.dist new file mode 100644 index 00000000..698915a1 --- /dev/null +++ b/plugins/wpgraphql-webhooks/.env.dist @@ -0,0 +1,38 @@ +DB_NAME=wordpress +DB_HOST=app_db +DB_USER=wordpress +DB_PASSWORD=wordpress +WP_TABLE_PREFIX=wp_ +WP_URL=http://localhost +WP_DOMAIN=localhost +ADMIN_EMAIL=admin@example.com +ADMIN_USERNAME=admin +ADMIN_PASSWORD=root +ADMIN_PATH=/wp-admin + +TEST_DB_NAME=wptests +TEST_DB_HOST=127.0.0.1 +TEST_DB_USER=root +TEST_DB_PASSWORD= +TEST_WP_TABLE_PREFIX=wp_ + +SKIP_DB_CREATE=false +TEST_WP_ROOT_FOLDER=/tmp/wordpress +TEST_ADMIN_EMAIL=admin@wp.test + +TESTS_DIR=tests +TESTS_OUTPUT=tests/_output +TESTS_DATA=tests/_data +TESTS_SUPPORT=tests/_support +TESTS_ENVS=tests/_envs + +CORE_BRANCH=develop +SKIP_TESTS_CLEANUP=1 +SUITES=wpunit + +WORDPRESS_DB_HOST=${DB_HOST} +WORDPRESS_DB_USER=${DB_USER} +WORDPRESS_DB_PASSWORD=${DB_PASSWORD} +WORDPRESS_DB_NAME=${DB_NAME} + +PLUGIN_SLUG=wpgraphql-webhooks \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/.gitattributes b/plugins/wpgraphql-webhooks/.gitattributes new file mode 100644 index 00000000..f9de9bfc --- /dev/null +++ b/plugins/wpgraphql-webhooks/.gitattributes @@ -0,0 +1,20 @@ + +/.github export-ignore +/assets export-ignore +/bin export-ignore +/docker export-ignore +/docs export-ignore +/phpstan export-ignore +/tests export-ignore + +/.coveralls export-ignore +/.distignore export-ignore +/.dockerignore export-ignore +/.env.dist export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.phpcs.xml.dist export-ignore +/composer.lock export-ignore +/phpstan.neon.dist export-ignore +/phpunit.xml.dist export-ignore +/psalm.xml export-ignore diff --git a/plugins/wpgraphql-webhooks/.gitignore b/plugins/wpgraphql-webhooks/.gitignore new file mode 100644 index 00000000..24d17ffd --- /dev/null +++ b/plugins/wpgraphql-webhooks/.gitignore @@ -0,0 +1,49 @@ +# Hidden files +.DS_Store +Thumbs.db + +# IDE Files +.devcontainer/* +.devcontainer.json +.vscode +.idea + +# Environment variables for testing +.env +.env.* +!.env.dist + +# Ruleset Overrides +phpcs.xml +phpunit.xml +phpstan.neon + +# Directory to generate the dist zipfile +plugin-build + +# Composer auth +auth.json + +# Composer deps +vendor + +# NPM deps +node_modules + +# Generated Schema used in some tooling. Versioned Schema is uploaded as a Release artifact to Github. +schema.graphql + +# WP CLI config overrides +wp-cli.local.yml + +# Tests +*.sql +*.tar.gz +!tests +tests/*.suite.yml +coverage/* +build/ +.log/ + +# Cache +phpcs-cache.json \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/.phpcs.xml.dist b/plugins/wpgraphql-webhooks/.phpcs.xml.dist new file mode 100644 index 00000000..e1068b77 --- /dev/null +++ b/plugins/wpgraphql-webhooks/.phpcs.xml.dist @@ -0,0 +1,323 @@ + + + Sniffs for the WPGraphQL plugin ecosystem + + + ./access-functions.php + ./wpgraphql-webhooks.php + ./activation.php + ./deactivation.php + ./src/ + */languages/* + */phpunit.xml* + **/tests/** + */vendor/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/wpgraphql-webhooks/README.md b/plugins/wpgraphql-webhooks/README.md new file mode 100644 index 00000000..dbd82d41 --- /dev/null +++ b/plugins/wpgraphql-webhooks/README.md @@ -0,0 +1,32 @@ +# WPGraphQL Webhooks + +## Overview + +A WordPress plugin that extends [WPGraphQL](https://www.wpgraphql.com/) to support webhook subscriptions and dispatching for headless WordPress environments. This allows external applications to listen and respond to WordPress content changes through GraphQL-driven webhook events. + +**⚠️ Work in Progress:** This plugin is under active development and not yet production-ready. + +## Features + +- Subscribe external services to WordPress content events +- Dispatch webhook requests when specified GraphQL mutations or actions occur +- Headless and API-friendly by design +- Designed for modern decoupled WordPress applications + +## Requirements + +- WordPress 6.0+ +- WPGraphQL 2.0.0+ +- PHP 7.4+ + +## Installation + +Clone the repository or download the latest release and place it in your WordPress `plugins` directory: + +## Documentation + +For detailed usage instructions, developer references, and examples, please visit the [Documentation](docs/index.md) folder included with this plugin. + + +## License +WP GPL 2 diff --git a/plugins/wpgraphql-webhooks/Sniffs/ControlStructures/ElseKeywordSniff.php b/plugins/wpgraphql-webhooks/Sniffs/ControlStructures/ElseKeywordSniff.php new file mode 100644 index 00000000..94842f74 --- /dev/null +++ b/plugins/wpgraphql-webhooks/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'); + } + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/access-functions.php b/plugins/wpgraphql-webhooks/access-functions.php new file mode 100644 index 00000000..ceecd9a5 --- /dev/null +++ b/plugins/wpgraphql-webhooks/access-functions.php @@ -0,0 +1,152 @@ + $args { + * Args for the webhook type. + * + * @type string $label Human-readable label for the webhook type (default: $type). + * @type string $description Description of the webhook type (default: ''). + * @type array $events Optional. List of events to register. + * @type array $config Optional. Additional configuration for the webhook. + * } + */ +if ( ! function_exists( 'register_webhook_type' ) ) { + function register_webhook_type( string $type, array $args = [] ): void { + /** @psalm-suppress HookNotFound */ + if ( did_action( 'graphql_register_webhooks' ) > 0 ) { + _doing_it_wrong( 'register_webhook_type', 'Call this before WebhookRegistry::init', '0.1.0' ); + + return; + } + + /** @psalm-suppress HookNotFound */ + add_action( + 'graphql_register_webhooks', + function (WebhookTypeRegistry $registry) use ($type, $args): void { + $events = []; + if ( ! empty( $args['events'] ) && is_array( $args['events'] ) ) { + foreach ( $args['events'] as $eventData ) { + $events[] = new Event( + $eventData['name'], + $eventData['hookName'], + $eventData['callback'] ?? null, + $eventData['priority'] ?? 10, + $eventData['argCount'] ?? 1 + ); + } + } + + $webhook = new WebhookDTO( + $type, + $args['label'] ?? '', + $args['description'] ?? '', + $args['config'] ?? [], + $events + ); + + $registry->register_webhook_type( $webhook ); + } + ); + } + +} + +/** + * Creates a new webhook + * + * @param string $type Webhook type identifier. + * @param string $name Webhook name/title. + * @param array $config Webhook configuration. + * + * @return int|\WP_Error Post ID of the new webhook or error. + */ +if ( ! function_exists( 'create_webhook' ) ) { + + /** + * @return \WP_Error|int + */ + function create_webhook( string $type, string $name, array $config = [] ) { // @phpstan-ignore missingType.iterableValue + return WebhookRegistry::instance()->create_webhook( $type, $name, $config ); + } + +} + +/** + * Gets a registered webhook type + * + * @param string $type Webhook type identifier. + * + * @return array|null Webhook type configuration or null if not found. + */ +if ( ! function_exists( 'get_webhook_type' ) ) { + + /** @phpstan-ignore missingType.iterableValue */ + function get_webhook_type( string $type ): ?array { + return WebhookRegistry::instance()->get_webhook_type( $type ); + } + +} + +/** + * Gets all registered webhook types + * + * @return array> All registered webhook types. + */ +if ( ! function_exists( 'get_webhook_types' ) ) { + + /** @phpstan-ignore missingType.iterableValue */ + function get_webhook_types(): array { + return WebhookRegistry::instance()->get_webhook_types(); + } + +} + +/** + * Registers a GraphQL event configuration to be attached to a WordPress action. + * + * This function schedules an event registration callback to be executed when + * the `graphql_register_events` action is fired (during `EventRegistry::init()`). + * + * The registered event will listen to a specified WordPress action (e.g. 'publish_post'), + * and execute a qualifying callback to potentially dispatch notifications or other side effects. + * + * @param Event $event The event object to register. + * + * @return void + */ +function register_graphql_event( Event $event ): void { + if ( did_action( 'graphql_register_events' ) ) { + _doing_it_wrong( + __FUNCTION__, + 'Call this before EventRegistry::init', + '0.0.1' + ); + return; + } + + add_action( + 'graphql_register_events', + static function (GraphQLEventRegistry $event_registry) use ($event) { + $event_registry->register_event( $event ); + } + ); +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/activation.php b/plugins/wpgraphql-webhooks/activation.php new file mode 100644 index 00000000..4034e816 --- /dev/null +++ b/plugins/wpgraphql-webhooks/activation.php @@ -0,0 +1,19 @@ + 1) { + $( this ).closest( '.webhook-header-row' ).remove(); + } else { + // Clear the values instead of removing the last row + $( this ).closest( '.webhook-header-row' ).find( 'input' ).val( '' ); + } + } + ); + + // Confirm webhook deletion + $( '.delete-webhook' ).on( + 'click', + function (e) { + if ( ! confirm( wpGraphQLWebhooks.confirmDelete )) { + e.preventDefault(); + } + } + ); + + // Handle test webhook clicks using event delegation + $( document ).on( + 'click', + '.test-webhook', + function (e) { + e.preventDefault(); + + var $link = $( this ); + var webhookId = $link.data( 'webhook-id' ); + var originalText = $link.text(); + + // Prevent multiple clicks + if ($link.hasClass( 'testing' )) { + return false; + } + + // Generate unique ID for this test result + var resultId = 'webhook-test-result-' + webhookId + '-' + Date.now(); + + // Update UI to show testing + $link.text( 'Testing...' ).addClass( 'testing' ); + + // Send test request + $.ajax({ + url: wpGraphQLWebhooks.ajaxUrl, + type: 'POST', + data: { + action: 'test_webhook', + webhook_id: webhookId, + nonce: wpGraphQLWebhooks.nonce + }, + success: function(response) { + if (response.success) { + $link.text( originalText ).removeClass( 'testing' ).addClass( 'success' ); + if (response.data) { + var $row = $link.closest( 'tr' ); + var colspan = $row.find( 'td, th' ).length; + + // Build detailed result HTML + var resultHtml = '
'; + resultHtml += ''; + resultHtml += '

' + response.data.message + '

'; + + // Response details + if (response.data.response_code) { + var statusClass = response.data.success ? 'success' : 'error'; + resultHtml += '

Response Status: ' + response.data.response_code + '

'; + } + + // Show payload sent + if (response.data.test_payload) { + resultHtml += '
'; + resultHtml += 'Test Payload Sent'; + resultHtml += '
' + JSON.stringify(response.data.test_payload, null, 2) + '
'; + resultHtml += '
'; + } + + // Show response body if available + if (response.data.response_body) { + resultHtml += '
'; + resultHtml += 'Response Body'; + resultHtml += '
' + response.data.response_body + '
'; + resultHtml += '
'; + } + + resultHtml += '
'; + + var noticeClass = response.data.success ? 'notice-success' : 'notice-warning'; + var $resultRow = $( '
' + resultHtml + '
' ); + $row.after( $resultRow ); + + // Handle dismiss button + $( '#' + resultId + ' .notice-dismiss' ).on( 'click', function() { + $( '#' + resultId ).fadeOut(function() { + $( this ).remove(); + }); + }); + } + } else { + $link.text( originalText ).removeClass( 'testing' ).addClass( 'error' ); + var errorData = response.data || {}; + var $row = $link.closest( 'tr' ); + var colspan = $row.find( 'td, th' ).length; + + // Build error HTML + var errorHtml = '
'; + errorHtml += ''; + errorHtml += '

Test failed: ' + (errorData.message || 'Unknown error') + '

'; + + if (errorData.error_code) { + errorHtml += '

Error Code: ' + errorData.error_code + '

'; + } + + if (errorData.error_data) { + errorHtml += '
'; + errorHtml += 'Error Details'; + errorHtml += '
' + JSON.stringify(errorData.error_data, null, 2) + '
'; + errorHtml += '
'; + } + + errorHtml += '
'; + + var $resultRow = $( '
' + errorHtml + '
' ); + $row.after( $resultRow ); + + // Handle dismiss button + $( '#' + resultId + ' .notice-dismiss' ).on( 'click', function() { + $( '#' + resultId ).fadeOut(function() { + $( this ).remove(); + }); + }); + } + }, + error: function(xhr, status, error) { + $link.text( originalText ).removeClass( 'testing' ).addClass( 'error' ); + var $row = $link.closest( 'tr' ); + var colspan = $row.find( 'td, th' ).length; + + var errorHtml = '
'; + errorHtml += ''; + errorHtml += '

Test error: ' + error + '

'; + errorHtml += '
'; + + var $resultRow = $( '
' + errorHtml + '
' ); + $row.after( $resultRow ); + + // Handle dismiss button + $( '#' + resultId + ' .notice-dismiss' ).on( 'click', function() { + $( '#' + resultId ).fadeOut(function() { + $( this ).remove(); + }); + }); + } + }); + } + ); + + // Handle bulk actions if using WP_List_Table + $('#doaction, #doaction2').on('click', function(e) { + var action = $(this).prev('select').val(); + + if (action === 'delete') { + var checked = $('input[name="webhook[]"]:checked'); + + if (checked.length === 0) { + alert('Please select at least one webhook to delete.'); + e.preventDefault(); + return false; + } + + if (!confirm(wpGraphQLWebhooks.confirmDelete)) { + e.preventDefault(); + return false; + } + } + }); + } + ); + +})( jQuery ); diff --git a/plugins/wpgraphql-webhooks/bin/_lib.sh b/plugins/wpgraphql-webhooks/bin/_lib.sh new file mode 100644 index 00000000..1f346531 --- /dev/null +++ b/plugins/wpgraphql-webhooks/bin/_lib.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set +u + +source ".env" + +## +# Add error message formatting to a string, and echo it. +# +# @param {string} message The string to add formatting to. +## +error_message() { + echo -en "\033[31mERROR\033[0m: $1" +} + +## +# Add warning message formatting to a string, and echo it. +# +# @param {string} message The string to add formatting to. +## +warning_message() { + echo -en "\033[33mWARNING\033[0m: $1" +} + +## +# Add status message formatting to a string, and echo it. +# +# @param {string} message The string to add formatting to. +## +status_message() { + echo -en "\033[32mSTATUS\033[0m: $1" +} + +## +# Download a file from a URL. +# +# @param {string} $1 The URL to download. +# @param {string} $2 The path to save the file. +## +download() { + if [ $(which curl) ]; then + curl -s "$1" >"$2" + elif [ $(which wget) ]; then + wget -nv -O "$2" "$1" + fi +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/bin/build-docker.sh b/plugins/wpgraphql-webhooks/bin/build-docker.sh new file mode 100644 index 00000000..76052351 --- /dev/null +++ b/plugins/wpgraphql-webhooks/bin/build-docker.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +set -eu + +## +# Use this script through NPM scripts in the package.json, after installing the NPM and Composer Dependencies and updating the .env file. +## +print_usage_instructions() { + echo "Usage: ./build-docker.sh [OPTIONS]" + echo "Options:" + echo " -c Build without cache" + echo " -h Display this help message" + echo "" + echo "Example use:" + echo " npm run docker:build" + echo "" + echo " WP_VERSION=6.7 PHP_VERSION=8.2 npm run docker:build -- - c" + echo "" + echo " WP_VERSION=6.7 PHP_VERSION=8.2 bin/build-docker.sh -- c" + exit 1 +} + + +if [[ ! -f ".env" ]]; then + echo "No .env file was detected. .env.dist has been copied to .env" + echo "Open the .env file and enter values to match your local environment" + cp .env.dist .env +fi + +source .env + +while getopts ":ch" opt; do + case ${opt} in + c) + echo "Build with --no-cache" + BUILD_NO_CACHE=--no-cache + ;; + h) + print_usage_instructions + ;; + \?) + echo "Invalid flag: -$OPTARG" 1>&2 + exit 1 + ;; + *) + print_usage_instructions + ;; + esac +done + + +TAG=${TAG:-latest} +WP_VERSION=${WP_VERSION:-6.7} +PHP_VERSION=${PHP_VERSION:-8.2} + +BUILD_NO_CACHE=${BUILD_NO_CACHE:-} + +docker build $BUILD_NO_CACHE \ + -t "${PLUGIN_SLUG}:${TAG}-wp${WP_VERSION}-php${PHP_VERSION}" \ + --build-arg WP_VERSION="${WP_VERSION}" \ + --build-arg PHP_VERSION="${PHP_VERSION}" \ + ./.docker \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/bin/install-plugins.sh b/plugins/wpgraphql-webhooks/bin/install-plugins.sh new file mode 100644 index 00000000..e609a1ce --- /dev/null +++ b/plugins/wpgraphql-webhooks/bin/install-plugins.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Exit if any command fails. +set -e + +source ".env" + +## Add the `wp plugin install` and `wp plugin activate` commands here for any external plugins that this one depends on for testing. +# +# Example: Install and activate WPGraphQL from the .org plugin repository. +# +# if ! $( wp plugin is-installed wp-graphql --allow-root ); then +# wp plugin install wp-graphql --allow-root +# fi +# wp plugin activate wp-graphql --allow-root +# +# Example: Install and activate the WPGraphQL Upload plugin from GitHub. +# +# if ! $( wp plugin is-installed wp-graphql-upload --allow-root ); then +# wp plugin install https://github.com/dre1080/wp-graphql-upload/archive/refs/heads/master.zip --allow-root +# fi +# wp plugin activate wp-graphql-upload --allow-root + +# We use an old version of WPGraphQL Content Blocks for testing the PUC. + +# WPGraphQL +install_wpgraphql() { + if ! $( wp plugin is-installed wp-graphql --allow-root ); then + wp plugin install wp-graphql --allow-root + fi + wp plugin activate wp-graphql --allow-root +} + +# Run the install functions. +cd $WORDPRESS_ROOT_DIR + +install_wpgraphql \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/bin/install-test-env.sh b/plugins/wpgraphql-webhooks/bin/install-test-env.sh new file mode 100644 index 00000000..ee7009eb --- /dev/null +++ b/plugins/wpgraphql-webhooks/bin/install-test-env.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash + +# Exit if any command fails. +set -e + +ORIGINAL_PATH=$(pwd) +BASEDIR=$(dirname "$0") + +# Include common environment variables and functions +source "${BASEDIR}/_lib.sh" + +# Common variables. +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") + +WP_DEBUG=${WP_DEBUG:-true} +SCRIPT_DEBUG=${SCRIPT_DEBUG:-true} +WP_VERSION=${WP_VERSION:-"latest"} + +## +# Install the database. +## +install_db() { + if [ "${SKIP_DB_CREATE}" = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${WORDPRESS_DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]} + local DB_SOCK_OR_PORT=${PARTS[1]} + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ]; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ]; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ]; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + echo -e "$(status_message "Creating the database (if it does not exist)...")" + + RESULT=$(mysql -u $WORDPRESS_DB_USER --password="$WORDPRESS_DB_PASSWORD" --skip-column-names -e "SHOW DATABASES LIKE '$WORDPRESS_DB_NAME'"$EXTRA) + if [ "$RESULT" != $WORDPRESS_DB_NAME ]; then + mysqladmin create $WORDPRESS_DB_NAME --user="$WORDPRESS_DB_USER" --password="$WORDPRESS_DB_PASSWORD"$EXTRA + fi +} + +download() { + if [ $(which curl) ]; then + curl -s "$1" >"$2" + elif [ $(which wget) ]; then + wget -nv -O "$2" "$1" + fi +} + +install_wordpress() { + # Create the WordPress root directory if it doesn't exist. + echo -e "$(status_message "Switching to the WordPress root directory $WORDPRESS_ROOT_DIR")" + mkdir -p "$WORDPRESS_ROOT_DIR" + cd "$WORDPRESS_ROOT_DIR" || { echo -e "$(error_message "Failed to enter directory: $WORDPRESS_ROOT_DIR")"; exit 1; } + + # Download WordPress + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WORDPRESS_ROOT_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=$(echo $WP_VERSION | sed 's/\./\\\\./g') + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WORDPRESS_ROOT_DIR + fi +} + +configure_wordpress() { + if [ "${SKIP_WP_SETUP}" = "true" ]; then + echo -e "$(warning_message "Skipping WordPress setup...")" + return 0 + fi + + cd $WORDPRESS_ROOT_DIR + + # Create a wp-config.php file if it doesn't exist. + if [ ! -f "wp-config.php" ]; then + echo -e "$(status_message "Creating wp-config.php file...")" + wp config create --dbname="$WORDPRESS_DB_NAME" --dbuser="$WORDPRESS_DB_USER" --dbpass="$WORDPRESS_DB_PASSWORD" --dbhost="$WORDPRESS_DB_HOST" --dbprefix="$WORDPRESS_TABLE_PREFIX" --allow-root + fi + + # Install WordPress. + echo -e "$(status_message "Installing WordPress...")" + + SITE_TITLE=${WORDPRESS_SITE_TITLE:-"Headless Login Tests"} + + wp core install --title="$SITE_TITLE" --admin_user="$WORDPRESS_ADMIN_USER" --admin_password="$WORDPRESS_ADMIN_PASSWORD" --admin_email="$WORDPRESS_ADMIN_EMAIL" --skip-email --url="$WORDPRESS_URL" --allow-root + + echo -e "$(status_message "Running WordPress version: $(wp core version --allow-root) at $(wp option get home --allow-root)")" +} + +setup_file_permissions() { + # Make sure the uploads and upgrade folders exist and we have permissions to add files. + echo -e "$(status_message "Ensuring that files can be uploaded...")" + + mkdir -p \ + wp-content/uploads \ + wp-content/upgrade + chmod 777 \ + wp-content \ + wp-content/plugins \ + wp-config.php \ + wp-settings.php \ + wp-content/uploads \ + wp-content/upgrade + + # Install a dummy favicon to avoid 404 errors. + echo -e "$(status_message "Installing a dummy favicon...")" + touch favicon.ico + chmod 767 favicon.ico +} + +setup_plugin() { + if [ "${SKIP_WP_SETUP}" = "true" ]; then + echo -e "$(warning_message "Skipping wpgraphql-webhooks installation...")" + return 0 + fi + + # Add this repo as a plugin to the repo + if [ ! -d $WORDPRESS_ROOT_DIR/wp-content/plugins/wpgraphql-webhooks ]; then + echo -e "$(status_message "Symlinking the plugin to the WordPress plugins directory...")" + + cd "$ORIGINAL_PATH" + ln -s "$(pwd)" "$WORDPRESS_ROOT_DIR/wp-content/plugins/$PLUGIN_SLUG" + fi + + cd "$ORIGINAL_PATH" + + # Install composer deps + echo -e "$(status_message "Installing Composer deps")" + composer install + + # Install npm deps + echo -e "$(status_message "Installing NPM Deps")" + npm install --no-audit --no-fund --no-progress + + # Build the plugin + npm run build +} + +post_setup() { + # Ensure we are in the WordPress root directory. + cd "$WORDPRESS_ROOT_DIR" + + # Activate the plugin. + echo -e "$(status_message "Activating the plugin...")" + wp plugin activate "$PLUGIN_SLUG" --allow-root + + # Set pretty permalinks. + echo -e "$(status_message "Setting permalink structure...")" + wp rewrite structure '/%year%/%monthnum%/%postname%/' --hard --allow-root + wp rewrite flush --allow-root + + wp config set WP_DEBUG true --raw --allow-root + wp config set WP_DEBUG_LOG true --raw --allow-root + wp config set GRAPHQL_DEBUG true --raw --allow-root + + wp core update-db --allow-root + + # Disable Update Checks + echo -e "$(status_message "Disabling update checks...")" + wp config set WP_AUTO_UPDATE_CORE false --raw --type=constant --quiet --allow-root + wp config set AUTOMATIC_UPDATER_DISABLED true --raw --type=constant --quiet --allow-root + + # Export the db for codeception to use + SQLDUMP="$WORDPRESS_ROOT_DIR/wp-content/plugins/$PLUGIN_SLUG/tests/_data/dump.sql" + mkdir -p "$(dirname "$SQLDUMP")" + if [ ! -f "$SQLDUMP" ]; then + echo -e "$(status_message "Exporting test database dump...")" + + wp db export "$SQLDUMP" --allow-root + fi + + echo -e "$(status_message "Installed plugins")" + wp plugin list --allow-root +} + +## +# The main function to install the test environment. +## + +echo "Installing test environment for WordPress ${WP_VERSION}..." + +# Create the database if it doesn't exist. +install_db + +# If this is the test site, we reset the database so no posts/comments/etc. +# dirty up the tests. +if [ "$1" == '--reset-site' ]; then + echo -e "$(status_message "Resetting test database...")" + wp db reset --yes --quiet --allow-root +fi + +install_wordpress +configure_wordpress +setup_file_permissions + +# Plugins are in a separate script to keep things clean. +echo -e "$(status_message "Installing external plugins...")" +cd "$ORIGINAL_PATH" + +bash "$ORIGINAL_PATH/$BASEDIR/install-plugins.sh" + +setup_plugin +post_setup \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/bin/run-codeception.sh b/plugins/wpgraphql-webhooks/bin/run-codeception.sh new file mode 100644 index 00000000..f7d8f22b --- /dev/null +++ b/plugins/wpgraphql-webhooks/bin/run-codeception.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash + +ORIGINAL_PATH=$(pwd) +BASEDIR=$(dirname "$0") +PROJECT_DIR="$WORDPRESS_ROOT_DIR/wp-content/plugins/$PLUGIN_SLUG" + + +source "${BASEDIR}/_lib.sh" + +echo -e "$(status_message "WordPress: ${WP_VERSION} PHP: ${PHP_VERSION}")" + +## +# Set up before running tests. +## +setup_before() { + cd "$PROJECT_DIR" + + # Download c3 for testing. + if [ ! -f "c3.php" ]; then + echo "Downloading Codeception's c3.php" + curl -L 'https://raw.github.com/Codeception/c3/2.0/c3.php' > "c3.php" + fi + + # Enable XDebug or PCOV for code coverage. + if [[ "$COVERAGE" == '1' ]]; then + if [[ "$USING_XDEBUG" == '1' ]]; then + echo "Enabling XDebug 3" + cp /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ + echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + else + echo "Using pcov/clobber for code coverage" + docker-php-ext-enable pcov + echo "pcov.enabled=1" >> /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + echo "pcov.directory=${PROJECT_DIR}" >> /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + COMPOSER_MEMORY_LIMIT=-1 composer require pcov/clobber --dev + vendor/bin/pcov clobber + fi + elif [[ -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ]]; then + echo "Disabling XDebug" + rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + fi + + # Install the PHP dev-dependencies. + echo "Running composer install" + COMPOSER_MEMORY_LIMIT=-1 composer install + +# Set output permission + echo "Setting Codeception output directory permissions" + chmod 777 -R tests/_output +} + +## +# Run tests. +## +run_tests() { + if [[ -n "$DEBUG" ]]; then + local debug="--debug" + fi + + local suites=$1 + if [[ -z "$SUITES" ]]; then + echo "No test suites specified. Must specify variable SUITES." + exit 1 + fi + + if [[ -n "$COVERAGE" ]]; then + local coverage="--coverage --coverage-xml $suites-coverage.xml" + fi + + # If maintenance mode is active, de-activate it + if $(wp maintenance-mode is-active --allow-root); then + echo "Deactivating maintenance mode" + wp maintenance-mode deactivate --allow-root + fi + + # Suites is the comma separated list of suites/tests to run. + echo "Running Test Suite $suites" + cd "$PROJECT_DIR" + XDEBUG_MODE=coverage vendor/bin/codecept run -c codeception.dist.yml ${suites} ${coverage:-} ${debug:-} --no-exit +} + +## +# Clean up after running tests. +## +cleanup_after() { + cd "$PROJECT_DIR" + + # Remove c3.php if it exists and cleanup is not skipped + if [ -f "c3.php" ] && [ "$SKIP_TESTS_CLEANUP" != "true" ]; then + echo "Removing Codeception's c3.php" + rm "c3.php" + fi + + # Disable XDebug or PCOV if they were enabled for code coverage + if [[ "$COVERAGE" == '1' ]]; then + if [[ "$USING_XDEBUG" == '1' ]]; then + echo "Disabling XDebug 3" + rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + else + echo "Disabling pcov/clobber" + docker-php-ext-disable pcov + sed -i '/pcov.enabled=1/d' /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + sed -i '/pcov.directory=${PROJECT_DIR}/d' /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + COMPOSER_MEMORY_LIMIT=-1 composer remove pcov/clobber --dev + fi + fi + + # Set output permission back to default + echo "Resetting Codeception output directory permissions" + chmod 777 -R tests/_output +} + +# Prepare to run tests. +echo "Setting up for Codeception tests" +setup_before + + +# Run the tests +run_tests $SUITES + +# Clean up after running tests. +echo "Cleaning up after Codeception tests" +cleanup_after + +# Check results and exit accordingly. +if [ -f "tests/_output/failed" ]; then + echo "Uh oh, Codeception tests failed." + exit 1 +else + echo "Woohoo! Codeception tests completed succesfully!" +fi \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/composer.json b/plugins/wpgraphql-webhooks/composer.json new file mode 100644 index 00000000..e052b5e8 --- /dev/null +++ b/plugins/wpgraphql-webhooks/composer.json @@ -0,0 +1,114 @@ +{ + "name": "wpengine/wpgraphql-webhooks", + "description": "Webhooks for WPGraphQL", + "type": "library", + "license": "GPL-3.0-or-later", + "authors": [ + { + "name": "WP Engine Headless OSS Development Team", + "email": "headless-oss@wpengine.com", + "homepage": "https://wpengine.com/" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "axepress/wp-graphql-plugin-boilerplate": "^0.1.0" + }, + "require-dev": { + "axepress/wp-graphql-cs": "^2.0.0", + "axepress/wp-graphql-stubs": "^2.3.0", + "phpcompatibility/php-compatibility": "dev-develop as 9.9.9", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan": "^2.0", + "slevomat/coding-standard": "^8.0", + "phpstan/phpstan-strict-rules": "^2.0", + "php-coveralls/php-coveralls": "^2.5", + "szepeviktor/phpstan-wordpress": "^2.0", + "humanmade/psalm-plugin-wordpress": "^3.1", + "wp-cli/wp-cli-bundle": "^2.8.1", + "wp-graphql/wp-graphql-testcase": "^3.0.1", + "codeception/lib-innerbrowser": "^1.0", + "codeception/module-asserts": "^1.0", + "codeception/module-cli": "^1.0", + "codeception/module-db": "^1.0", + "codeception/module-filesystem": "^1.0", + "codeception/module-phpbrowser": "^1.0", + "codeception/module-rest": "^2.0", + "codeception/module-webdriver": "^1.0", + "codeception/phpunit-wrapper": "^9.0", + "lucatume/wp-browser": "^3.7", + "mockery/mockery": "^1.5" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true, + "cweagans/composer-patches": true + }, + "optimize-autoloader": true, + "classmap-authoritative": true, + "process-timeout": 0, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "WPGraphQL\\Webhooks\\": "src/" + }, + "files": [ + "access-functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "HWPStandard\\": "phpcs/HWPStandard", + "WPGraphQL\\Webhooks\\Rules\\": "phpstan/Rules/" + } + }, + "extra": { + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + } + }, + "scripts": { + "install-test-env": "bash bin/install-test-env.sh", + "delete-vendor-files": "rm -rf composer.lock vendor", + "docker:build": "bash bin/build-docker.sh", + "docker:start": "@docker:build && @docker:up", + "docker:stop": "docker compose down --volumes", + "docker:up": "sh -c 'composer docker:stop' && docker compose --env-file .env up --detach", + "lint": "vendor/bin/phpcs", + "phpcs-i": [ + "php ./vendor/bin/phpcs -i" + ], + "check-cs": [ + "php ./vendor/bin/phpcs" + ], + "fix-cs": [ + "php ./vendor/bin/phpcbf" + ], + "phpstan": [ + "vendor/bin/phpstan analyze --ansi --memory-limit=1G" + ], + "php:psalm": "psalm", + "php:psalm:info": "psalm --show-info=true", + "php:psalm:fix": "psalm --alter" + }, + "archive": { + "name": "wpgraphql-webhooks", + "exclude": [ + "/.*", + "/assets", + "/bin", + "/composer.lock", + "/phpstan.neon.dist", + "/psalm.xml", + "/README.md" + ] + }, + "provide": { + "lucatume/wp-browser": "3.7.*" + } +} diff --git a/plugins/wpgraphql-webhooks/composer.lock b/plugins/wpgraphql-webhooks/composer.lock new file mode 100644 index 00000000..ac224b43 --- /dev/null +++ b/plugins/wpgraphql-webhooks/composer.lock @@ -0,0 +1,11243 @@ +{ + "_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": "c95ce760de9d78f5a1b0c1e16bbc33a4", + "packages": [ + { + "name": "axepress/wp-graphql-plugin-boilerplate", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/AxeWP/wp-graphql-plugin-boilerplate.git", + "reference": "09495b61346453baabdf4c71a38ada3cfc91c3a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/AxeWP/wp-graphql-plugin-boilerplate/zipball/09495b61346453baabdf4c71a38ada3cfc91c3a7", + "reference": "09495b61346453baabdf4c71a38ada3cfc91c3a7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "axepress/wp-graphql-cs": "^2.0.0", + "axepress/wp-graphql-stubs": "^2.3.0", + "phpcompatibility/php-compatibility": "dev-develop as 9.9.9", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "szepeviktor/phpstan-wordpress": "^2.0", + "wp-cli/wp-cli-bundle": "^2.8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "AxeWP\\GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "AxePress Development", + "homepage": "https://axepress.dev" + }, + { + "name": "David Levine", + "role": "Developer" + } + ], + "description": "Boilerplate for creating WPGraphQL extensions", + "support": { + "issues": "https://github.com/AxeWP/wp-graphql-plugin-boilerplate/issues", + "source": "https://github.com/AxeWP/wp-graphql-plugin-boilerplate/tree/0.1.1" + }, + "funding": [ + { + "url": "https://github.com/AxeWp", + "type": "github" + } + ], + "time": "2025-06-07T02:03:50+00:00" + } + ], + "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": "axepress/wp-graphql-cs", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/AxeWP/WPGraphQL-Coding-Standards.git", + "reference": "2107d7ae6d05759820b57d0b00125b43eeb7c2d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/AxeWP/WPGraphQL-Coding-Standards/zipball/2107d7ae6d05759820b57d0b00125b43eeb7c2d2", + "reference": "2107d7ae6d05759820b57d0b00125b43eeb7c2d2", + "shasum": "" + }, + "require": { + "automattic/vipwpcs": "^3.0", + "php": ">=7.2", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "slevomat/coding-standard": "^8.12" + }, + "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" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "AxePress Development", + "email": "support@axepress.dev", + "homepage": "https://axepress.dev" + }, + { + "name": "David Levine", + "role": "Developer" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) for the WPGraphQL ecosystem.", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress", + "wpcs", + "wpgraphql" + ], + "support": { + "issues": "https://github.com/AxeWP/WPGraphQL-Coding-Standards/issues", + "source": "https://github.com/AxeWP/WPGraphQL-Coding-Standards" + }, + "time": "2025-05-03T00:30:19+00:00" + }, + { + "name": "axepress/wp-graphql-stubs", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/AxeWP/wp-graphql-stubs.git", + "reference": "6ca5c5ee37012743e09e2f9e7c3c272c5e273bb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/AxeWP/wp-graphql-stubs/zipball/6ca5c5ee37012743e09e2f9e7c3c272c5e273bb8", + "reference": "6ca5c5ee37012743e09e2f9e7c3c272c5e273bb8", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": ">=5.4" + }, + "require-dev": { + "php": ">=7.4", + "php-stubs/generator": "^0.8.0", + "phpstan/phpstan": "^2.1.5" + }, + "suggest": { + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WPGraphQL function and class declaration stubs for static analysis.", + "homepage": "https://github.com/axewp/wp-graphql-stubs", + "keywords": [ + "PHPStan", + "graphql", + "static analysis", + "wordpress", + "wp-graphql", + "wpgraphql" + ], + "support": { + "issues": "https://github.com/AxeWP/wp-graphql-stubs/issues", + "source": "https://github.com/AxeWP/wp-graphql-stubs/tree/v2.3.3" + }, + "funding": [ + { + "url": "https://github.com/AxeWp", + "type": "github" + } + ], + "time": "2025-06-18T02:06:46+00:00" + }, + { + "name": "behat/gherkin", + "version": "v4.10.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "shasum": "" + }, + "require": { + "php": "~7.2|~8.0" + }, + "require-dev": { + "cucumber/cucumber": "dev-gherkin-24.1.0", + "phpunit/phpunit": "~8|~9", + "symfony/yaml": "~3|~4|~5|~6|~7" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.10.0" + }, + "time": "2024-10-19T14:46:06+00:00" + }, + { + "name": "codeception/codeception", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Codeception.git", + "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/b88014f3348c93f3df99dc6d0967b0dbfa804474", + "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474", + "shasum": "" + }, + "require": { + "behat/gherkin": "^4.4.0", + "codeception/lib-asserts": "^1.0 | 2.0.*@dev", + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", + "codeception/stub": "^2.0 | ^3.0 | ^4.0", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/psr7": "^1.4 | ^2.0", + "php": ">=5.6.0 <9.0", + "symfony/console": ">=2.7 <6.0", + "symfony/css-selector": ">=2.7 <6.0", + "symfony/event-dispatcher": ">=2.7 <6.0", + "symfony/finder": ">=2.7 <6.0", + "symfony/yaml": ">=2.7 <6.0" + }, + "require-dev": { + "codeception/module-asserts": "^1.0 | 2.0.*@dev", + "codeception/module-cli": "^1.0 | 2.0.*@dev", + "codeception/module-db": "^1.0 | 2.0.*@dev", + "codeception/module-filesystem": "^1.0 | 2.0.*@dev", + "codeception/module-phpbrowser": "^1.0 | 2.0.*@dev", + "codeception/specify": "~0.3", + "codeception/util-universalframework": "*@dev", + "monolog/monolog": "~1.8", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <6.0", + "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" + }, + "suggest": { + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "hoa/console": "For interactive console functionality", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" + }, + "bin": [ + "codecept" + ], + "type": "library", + "extra": { + "branch-alias": [] + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Codeception\\": "src/Codeception", + "Codeception\\Extension\\": "ext" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "https://codegyre.com" + } + ], + "description": "BDD-style testing framework", + "homepage": "https://codeception.com/", + "keywords": [ + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" + ], + "support": { + "issues": "https://github.com/Codeception/Codeception/issues", + "source": "https://github.com/Codeception/Codeception/tree/4.2.2" + }, + "funding": [ + { + "url": "https://opencollective.com/codeception", + "type": "open_collective" + } + ], + "time": "2022-08-13T13:28:25+00:00" + }, + { + "name": "codeception/lib-asserts", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/lib-asserts.git", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", + "shasum": "" + }, + "require": { + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", + "ext-dom": "*", + "php": ">=5.6.0 <9.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + }, + { + "name": "Gintautas Miselis" + }, + { + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" + } + ], + "description": "Assertion methods used by Codeception core and Asserts module", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/lib-asserts/issues", + "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" + }, + "time": "2020-10-21T16:26:20+00:00" + }, + { + "name": "codeception/lib-innerbrowser", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/lib-innerbrowser.git", + "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", + "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", + "shasum": "" + }, + "require": { + "codeception/codeception": "4.*@dev", + "ext-dom": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.6.0 <9.0", + "symfony/browser-kit": ">=2.7 <6.0", + "symfony/dom-crawler": ">=2.7 <6.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "require-dev": { + "codeception/util-universalframework": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "Parent library for all Codeception framework modules and PhpBrowser", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/lib-innerbrowser/issues", + "source": "https://github.com/Codeception/lib-innerbrowser/tree/1.5.1" + }, + "time": "2021-08-30T15:21:42+00:00" + }, + { + "name": "codeception/module-asserts", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-asserts.git", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", + "shasum": "" + }, + "require": { + "codeception/codeception": "*@dev", + "codeception/lib-asserts": "^1.13.1", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + }, + { + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" + } + ], + "description": "Codeception module containing various assertions", + "homepage": "https://codeception.com/", + "keywords": [ + "assertions", + "asserts", + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/module-asserts/issues", + "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" + }, + "time": "2020-10-21T16:48:15+00:00" + }, + { + "name": "codeception/module-cli", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-cli.git", + "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-cli/zipball/1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f", + "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f", + "shasum": "" + }, + "require": { + "codeception/codeception": "*@dev", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + } + ], + "description": "Codeception module for testing basic shell commands and shell output", + "homepage": "http://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/module-cli/issues", + "source": "https://github.com/Codeception/module-cli/tree/1.1.1" + }, + "time": "2020-12-26T16:56:19+00:00" + }, + { + "name": "codeception/module-db", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-db.git", + "reference": "04c3e66fbd3a3ced17fcccc49627f6393a97b04b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-db/zipball/04c3e66fbd3a3ced17fcccc49627f6393a97b04b", + "reference": "04c3e66fbd3a3ced17fcccc49627f6393a97b04b", + "shasum": "" + }, + "require": { + "codeception/codeception": "*@dev", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "DB module for Codeception", + "homepage": "http://codeception.com/", + "keywords": [ + "codeception", + "database-testing", + "db-testing" + ], + "support": { + "issues": "https://github.com/Codeception/module-db/issues", + "source": "https://github.com/Codeception/module-db/tree/1.2.0" + }, + "time": "2022-03-05T19:38:40+00:00" + }, + { + "name": "codeception/module-filesystem", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-filesystem.git", + "reference": "781be167fb1557bfc9b61e0a4eac60a32c534ec1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-filesystem/zipball/781be167fb1557bfc9b61e0a4eac60a32c534ec1", + "reference": "781be167fb1557bfc9b61e0a4eac60a32c534ec1", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.0", + "php": ">=5.6.0 <9.0", + "symfony/finder": ">=2.7 <6.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "Codeception module for testing local filesystem", + "homepage": "http://codeception.com/", + "keywords": [ + "codeception", + "filesystem" + ], + "support": { + "issues": "https://github.com/Codeception/module-filesystem/issues", + "source": "https://github.com/Codeception/module-filesystem/tree/1.0.3" + }, + "time": "2020-10-24T14:46:40+00:00" + }, + { + "name": "codeception/module-phpbrowser", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-phpbrowser.git", + "reference": "8ba6bede11d0914e74d98691f427fd8f397f192e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-phpbrowser/zipball/8ba6bede11d0914e74d98691f427fd8f397f192e", + "reference": "8ba6bede11d0914e74d98691f427fd8f397f192e", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.1", + "codeception/lib-innerbrowser": "^1.3", + "guzzlehttp/guzzle": "^6.3|^7.0", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "require-dev": { + "codeception/module-rest": "^1.0" + }, + "suggest": { + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "Codeception module for testing web application over HTTP", + "homepage": "http://codeception.com/", + "keywords": [ + "codeception", + "functional-testing", + "http" + ], + "support": { + "issues": "https://github.com/Codeception/module-phpbrowser/issues", + "source": "https://github.com/Codeception/module-phpbrowser/tree/1.0.3" + }, + "time": "2022-05-21T13:50:41+00:00" + }, + { + "name": "codeception/module-rest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-rest.git", + "reference": "ee4ea06cd8a5057f24f37f8bf25b6815ddc77840" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-rest/zipball/ee4ea06cd8a5057f24f37f8bf25b6815ddc77840", + "reference": "ee4ea06cd8a5057f24f37f8bf25b6815ddc77840", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.1", + "ext-dom": "*", + "ext-json": "*", + "justinrainbow/json-schema": "~5.2.9", + "php": "^7.4 | ^8.0", + "softcreatr/jsonpath": "^0.5 | ^0.7 | ^0.8" + }, + "require-dev": { + "codeception/lib-innerbrowser": "^2.0", + "codeception/stub": "^4.0", + "codeception/util-universalframework": "^1.0" + }, + "suggest": { + "aws/aws-sdk-php": "For using AWS Auth" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gintautas Miselis" + } + ], + "description": "REST module for Codeception", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception", + "rest" + ], + "support": { + "issues": "https://github.com/Codeception/module-rest/issues", + "source": "https://github.com/Codeception/module-rest/tree/2.0.3" + }, + "time": "2023-03-10T19:23:22+00:00" + }, + { + "name": "codeception/module-webdriver", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-webdriver.git", + "reference": "e22ac7da756df659df6dd4fac2dff9c859e30131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-webdriver/zipball/e22ac7da756df659df6dd4fac2dff9c859e30131", + "reference": "e22ac7da756df659df6dd4fac2dff9c859e30131", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.0", + "php": ">=5.6.0 <9.0", + "php-webdriver/webdriver": "^1.8.0" + }, + "suggest": { + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + }, + { + "name": "Zaahid Bateson" + } + ], + "description": "WebDriver module for Codeception", + "homepage": "http://codeception.com/", + "keywords": [ + "acceptance-testing", + "browser-testing", + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/module-webdriver/issues", + "source": "https://github.com/Codeception/module-webdriver/tree/1.4.1" + }, + "time": "2022-09-12T05:09:51+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "9.0.9", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "7439a53ae367986e9c22b2ac00f9d7376bb2f8cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7439a53ae367986e9c22b2ac00f9d7376bb2f8cf", + "reference": "7439a53ae367986e9c22b2ac00f9d7376bb2f8cf", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "phpunit/phpunit": "^9.0" + }, + "require-dev": { + "codeception/specify": "*", + "consolidation/robo": "^3.0.0-alpha3", + "vlucas/phpdotenv": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + }, + { + "name": "Naktibalda" + } + ], + "description": "PHPUnit classes used by Codeception", + "support": { + "issues": "https://github.com/Codeception/phpunit-wrapper/issues", + "source": "https://github.com/Codeception/phpunit-wrapper/tree/9.0.9" + }, + "abandoned": true, + "time": "2022-05-23T06:24:11+00:00" + }, + { + "name": "codeception/stub", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/18a148dacd293fc7b044042f5aa63a82b08bff5d", + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | 10.0.x-dev" + }, + "require-dev": { + "consolidation/robo": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "support": { + "issues": "https://github.com/Codeception/Stub/issues", + "source": "https://github.com/Codeception/Stub/tree/4.0.2" + }, + "time": "2022-01-31T19:25:15+00:00" + }, + { + "name": "composer/ca-bundle", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "d665d22c417056996c59019579f1967dfe5c1e82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d665d22c417056996c59019579f1967dfe5c1e82", + "reference": "d665d22c417056996c59019579f1967dfe5c1e82", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.7" + }, + "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": "2025-05-26T15:08:54+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34", + "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.6.1" + }, + "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": "2025-03-24T13:50:44+00:00" + }, + { + "name": "composer/composer", + "version": "2.7.7", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "291942978f39435cf904d33739f98d7d4eca7b23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/291942978f39435cf904d33739f98d7d4eca7b23", + "reference": "291942978f39435cf904d33739f98d7d4eca7b23", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "composer/class-map-generator": "^1.3.3", + "composer/metadata-minifier": "^1.0", + "composer/pcre": "^2.1 || ^3.1", + "composer/semver": "^3.3", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^5.2.11", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "react/promise": "^2.8 || ^3", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "seld/signal-handler": "^2.0", + "symfony/console": "^5.4.11 || ^6.0.11 || ^7", + "symfony/filesystem": "^5.4 || ^6.0 || ^7", + "symfony/finder": "^5.4 || ^6.0 || ^7", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "symfony/process": "^5.4 || ^6.0 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.0", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.0", + "phpstan/phpstan-symfony": "^1.4.0", + "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + }, + "branch-alias": { + "dev-main": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy", + "source": "https://github.com/composer/composer/tree/2.7.7" + }, + "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-06-10T20:11:12+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "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": "2021-04-07T13:37:33+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/spdx-licenses", + "version": "1.5.9", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/edf364cefe8c43501e21e88110aac10b284c3c9f", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f", + "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": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "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": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.9" + }, + "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": "2025-05-12T21:07:07+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": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "eftec/bladeone", + "version": "3.52", + "source": { + "type": "git", + "url": "https://github.com/EFTEC/BladeOne.git", + "reference": "a19bf66917de0b29836983db87a455a4f6e32148" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EFTEC/BladeOne/zipball/a19bf66917de0b29836983db87a455a4f6e32148", + "reference": "a19bf66917de0b29836983db87a455a4f6e32148", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16.1", + "phpunit/phpunit": "^5.7", + "squizlabs/php_codesniffer": "^3.5.4" + }, + "suggest": { + "eftec/bladeonehtml": "Extension to create forms", + "ext-mbstring": "This extension is used if it's active" + }, + "type": "library", + "autoload": { + "psr-4": { + "eftec\\bladeone\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jorge Patricio Castro Castillo", + "email": "jcastro@eftec.cl" + } + ], + "description": "The standalone version Blade Template Engine from Laravel in a single php file", + "homepage": "https://github.com/EFTEC/BladeOne", + "keywords": [ + "blade", + "php", + "template", + "templating", + "view" + ], + "support": { + "issues": "https://github.com/EFTEC/BladeOne/issues", + "source": "https://github.com/EFTEC/BladeOne/tree/3.52" + }, + "time": "2021-04-17T13:49:01+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": "gettext/gettext", + "version": "v4.8.12", + "source": { + "type": "git", + "url": "https://github.com/php-gettext/Gettext.git", + "reference": "11af89ee6c087db3cf09ce2111a150bca5c46e12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/11af89ee6c087db3cf09ce2111a150bca5c46e12", + "reference": "11af89ee6c087db3cf09ce2111a150bca5c46e12", + "shasum": "" + }, + "require": { + "gettext/languages": "^2.3", + "php": ">=5.4.0" + }, + "require-dev": { + "illuminate/view": "^5.0.x-dev", + "phpunit/phpunit": "^4.8|^5.7|^6.5", + "squizlabs/php_codesniffer": "^3.0", + "symfony/yaml": "~2", + "twig/extensions": "*", + "twig/twig": "^1.31|^2.0" + }, + "suggest": { + "illuminate/view": "Is necessary if you want to use the Blade extractor", + "symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator", + "twig/extensions": "Is necessary if you want to use the Twig extractor", + "twig/twig": "Is necessary if you want to use the Twig extractor" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gettext\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oscar Otero", + "email": "oom@oscarotero.com", + "homepage": "http://oscarotero.com", + "role": "Developer" + } + ], + "description": "PHP gettext manager", + "homepage": "https://github.com/oscarotero/Gettext", + "keywords": [ + "JS", + "gettext", + "i18n", + "mo", + "po", + "translation" + ], + "support": { + "email": "oom@oscarotero.com", + "issues": "https://github.com/oscarotero/Gettext/issues", + "source": "https://github.com/php-gettext/Gettext/tree/v4.8.12" + }, + "funding": [ + { + "url": "https://paypal.me/oscarotero", + "type": "custom" + }, + { + "url": "https://github.com/oscarotero", + "type": "github" + }, + { + "url": "https://www.patreon.com/misteroom", + "type": "patreon" + } + ], + "time": "2024-05-18T10:25:07+00:00" + }, + { + "name": "gettext/languages", + "version": "2.12.1", + "source": { + "type": "git", + "url": "https://github.com/php-gettext/Languages.git", + "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-gettext/Languages/zipball/0b0b0851c55168e1dfb14305735c64019732b5f1", + "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4" + }, + "bin": [ + "bin/export-plural-rules", + "bin/import-cldr-data" + ], + "type": "library", + "autoload": { + "psr-4": { + "Gettext\\Languages\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michele Locati", + "email": "mlocati@gmail.com", + "role": "Developer" + } + ], + "description": "gettext languages with plural rules", + "homepage": "https://github.com/php-gettext/Languages", + "keywords": [ + "cldr", + "i18n", + "internationalization", + "l10n", + "language", + "languages", + "localization", + "php", + "plural", + "plural rules", + "plurals", + "translate", + "translations", + "unicode" + ], + "support": { + "issues": "https://github.com/php-gettext/Languages/issues", + "source": "https://github.com/php-gettext/Languages/tree/2.12.1" + }, + "funding": [ + { + "url": "https://paypal.me/mlocati", + "type": "custom" + }, + { + "url": "https://github.com/mlocati", + "type": "github" + } + ], + "time": "2025-03-19T11:14:02+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:37:11+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:27:01+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-03-27T12:30:47+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+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": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/jsonrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/jsonrainbow/json-schema/issues", + "source": "https://github.com/jsonrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, + { + "name": "mck89/peast", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/mck89/peast.git", + "reference": "3a752d39bd7d8dc1e19bcf424f3d5ac1a1ca6ad5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mck89/peast/zipball/3a752d39bd7d8dc1e19bcf424f3d5ac1a1ca6ad5", + "reference": "3a752d39bd7d8dc1e19bcf424f3d5ac1a1ca6ad5", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17.0-dev" + } + }, + "autoload": { + "psr-4": { + "Peast\\": "lib/Peast/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Marco Marchiò", + "email": "marco.mm89@gmail.com" + } + ], + "description": "Peast is PHP library that generates AST for JavaScript code", + "support": { + "issues": "https://github.com/mck89/peast/issues", + "source": "https://github.com/mck89/peast/tree/v1.17.0" + }, + "time": "2025-03-07T19:44:14+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-04-29T12:36:36+00:00" + }, + { + "name": "nb/oxymel", + "version": "v0.1.0", + "source": { + "type": "git", + "url": "https://github.com/nb/oxymel.git", + "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nb/oxymel/zipball/cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", + "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "autoload": { + "psr-0": { + "Oxymel": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nikolay Bachiyski", + "email": "nb@nikolay.bg", + "homepage": "http://extrapolate.me/" + } + ], + "description": "A sweet XML builder", + "homepage": "https://github.com/nb/oxymel", + "keywords": [ + "xml" + ], + "support": { + "issues": "https://github.com/nb/oxymel/issues", + "source": "https://github.com/nb/oxymel/tree/master" + }, + "time": "2013-02-24T15:01:54+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": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-coveralls/php-coveralls", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/php-coveralls/php-coveralls.git", + "reference": "00b9fce4d785a98760ca02f305c197f5fcfb6004" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/00b9fce4d785a98760ca02f305c197f5fcfb6004", + "reference": "00b9fce4d785a98760ca02f305c197f5fcfb6004", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "php": "^7.0 || ^8.0", + "psr/log": "^1.0 || ^2.0", + "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^1.1 || ^2.3", + "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || >=8.0 <8.5.29 || >=9.0 <9.5.23", + "sanmai/phpunit-legacy-adapter": "^6.1 || ^8.0" + }, + "suggest": { + "symfony/http-kernel": "Allows Symfony integration" + }, + "bin": [ + "bin/php-coveralls" + ], + "type": "library", + "autoload": { + "psr-4": { + "PhpCoveralls\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kitamura Satoshi", + "email": "with.no.parachute@gmail.com", + "homepage": "https://www.facebook.com/satooshi.jp", + "role": "Original creator" + }, + { + "name": "Takashi Matsuo", + "email": "tmatsuo@google.com" + }, + { + "name": "Google Inc" + }, + { + "name": "Dariusz Ruminski", + "email": "dariusz.ruminski@gmail.com", + "homepage": "https://github.com/keradus" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-coveralls/php-coveralls/graphs/contributors" + } + ], + "description": "PHP client library for Coveralls API", + "homepage": "https://github.com/php-coveralls/php-coveralls", + "keywords": [ + "ci", + "coverage", + "github", + "test" + ], + "support": { + "issues": "https://github.com/php-coveralls/php-coveralls/issues", + "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.8.0" + }, + "time": "2025-05-12T08:35:27+00:00" + }, + { + "name": "php-extended/polyfill-php80-str-utils", + "version": "1.3.7", + "source": { + "type": "git", + "url": "https://gitlab.com/php-extended/polyfill-php80-str-utils.git", + "reference": "0749426252e3e27c526fda939e8d3ff050bf907b" + }, + "dist": { + "type": "zip", + "url": "https://gitlab.com/api/v4/projects/php-extended%2Fpolyfill-php80-str-utils/repository/archive.zip?sha=0749426252e3e27c526fda939e8d3ff050bf907b", + "reference": "0749426252e3e27c526fda939e8d3ff050bf907b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "php-extended/placeholder-phpunit": "^9" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "PhpExtended\\Polyfill\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anastaszor", + "homepage": "https://gitlab.com/Anastaszor", + "role": "developer" + } + ], + "description": "A php implementation of string functions introduced in php8 and above", + "homepage": "https://gitlab.com/php-extended/polyfill-str-utils", + "keywords": [ + "implementation", + "php", + "polyfill", + "str", + "string_ends_with", + "string_starts_with", + "utils" + ], + "support": { + "issues": "https://gitlab.com/php-extended/polyfill-str-utils/issues", + "source": "https://gitlab.com/php-extended/polyfill-str-utils" + }, + "abandoned": "php >= 8.0", + "time": "2024-03-31T13:28:10+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.1", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "92e444847d94f7c30f88c60004648f507688acd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/92e444847d94f7c30f88c60004648f507688acd5", + "reference": "92e444847d94f7c30f88c60004648f507688acd5", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.4", + "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.1" + }, + "time": "2025-05-02T12:33:34+00:00" + }, + { + "name": "php-stubs/wp-cli-stubs", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wp-cli-stubs.git", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/af16401e299a3fd2229bd0fa9a037638a4174a9d", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0" + }, + "require-dev": { + "php": "~7.3 || ~8.0", + "php-stubs/generator": "^0.8.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wp-cli-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress", + "wp-cli" + ], + "support": { + "issues": "https://github.com/php-stubs/wp-cli-stubs/issues", + "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.12.0" + }, + "time": "2025-06-10T09:58:05+00:00" + }, + { + "name": "php-webdriver/webdriver", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/998e499b786805568deaf8cbf06f4044f05d91bf", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^7.3 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^5.0 || ^6.0 || ^7.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.20.0", + "ondram/ci-detector": "^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.2" + }, + "time": "2024-11-21T15:12:59+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "dev-develop", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9013cd039fe5740953f9fdeebd19d901b80e26f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9013cd039fe5740953f9fdeebd19d901b80e26f2", + "reference": "9013cd039fe5740953f9fdeebd19d901b80e26f2", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0.12", + "squizlabs/php_codesniffer": "^3.10.0" + }, + "replace": { + "wimg/php-compatibility": "*" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.1.3", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4 || ^10.1.0", + "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0" + }, + "suggest": { + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "default-branch": true, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev", + "dev-develop": "10.x-dev" + } + }, + "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", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibility/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-01-20T20:06:48+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.7", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-05-12T16:38:37+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/fa4b8d051e278072928e32d817456a7fdb57b6ca", + "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.1.0", + "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-06-14T07:40:39+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/65355670ac17c34cd235cf9d3ceae1b9252c4dad", + "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "phpcs4", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-06-12T04:32:33+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/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "time": "2024-09-04T20:21:43+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.17", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", + "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-05-21T20:55:28+00:00" + }, + { + "name": "phpstan/phpstan-deprecation-rules", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", + "reference": "468e02c9176891cc901143da118f09dc9505fc2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f", + "reference": "468e02c9176891cc901143da118f09dc9505fc2f", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.15" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "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": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3" + }, + "time": "2025-05-14T10:56:57+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": "phpunit/php-code-coverage", + "version": "9.2.32", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.23", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-05-02T06:40:34+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/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "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": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2024-07-11T14:55:45+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + }, + { + "name": "seld/signal-handler", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\Signal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", + "keywords": [ + "posix", + "sigint", + "signal", + "sigterm", + "unix" + ], + "support": { + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2" + }, + "time": "2023-09-03T09:24:00+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.19.1", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/458d665acd49009efebd7e0cb385d71ae9ac3220", + "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220", + "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.13.0" + }, + "require-dev": { + "phing/phing": "3.0.1", + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/phpstan": "2.1.17", + "phpstan/phpstan-deprecation-rules": "2.0.3", + "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.21|12.1.3" + }, + "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.19.1" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2025-06-09T17:53:57+00:00" + }, + { + "name": "softcreatr/jsonpath", + "version": "0.7.6", + "source": { + "type": "git", + "url": "https://github.com/SoftCreatR/JSONPath.git", + "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/e04c02cb78bcc242c69d17dac5b29436bf3e1076", + "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1,<8.0" + }, + "replace": { + "flow/jsonpath": "*" + }, + "require-dev": { + "phpunit/phpunit": ">=7.0", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Flow\\JSONPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com", + "homepage": "https://prismaticbytes.com", + "role": "Developer" + }, + { + "name": "Sascha Greuel", + "email": "hello@1-2.dev", + "homepage": "https://1-2.dev", + "role": "Developer" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "support": { + "email": "hello@1-2.dev", + "forum": "https://github.com/SoftCreatR/JSONPath/discussions", + "issues": "https://github.com/SoftCreatR/JSONPath/issues", + "source": "https://github.com/SoftCreatR/JSONPath" + }, + "funding": [ + { + "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", + "type": "custom" + }, + { + "url": "https://github.com/softcreatr", + "type": "github" + } + ], + "time": "2022-09-27T09:27:12+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.13.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", + "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-06-17T22:17:01+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "03cce39764429e07fbab9b989a1182a24578341d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/03cce39764429e07fbab9b989a1182a24578341d", + "reference": "03cce39764429e07fbab9b989a1182a24578341d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "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": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/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/config", + "version": "v5.4.46", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "977c88a02d7d3f16904a81907531b19666a08e78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/977c88a02d7d3f16904a81907531b19666a08e78", + "reference": "977c88a02d7d3f16904a81907531b19666a08e78", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.46" + }, + "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-30T07:58:02+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/css-selector", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "4f7f3c35fba88146b56d0025d20ace3f3901f097" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4f7f3c35fba88146b56d0025d20ace3f3901f097", + "reference": "4f7f3c35fba88146b56d0025d20ace3f3901f097", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/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-09-25T14:11:13+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/dom-crawler", + "version": "v5.4.48", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "b57df76f4757a9a8dfbb57ba48d7780cc20776c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b57df76f4757a9a8dfbb57ba48d7780cc20776c6", + "reference": "b57df76f4757a9a8dfbb57ba48d7780cc20776c6", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "masterminds/html5": "<2.6" + }, + "require-dev": { + "masterminds/html5": "^2.6", + "symfony/css-selector": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "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 DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v5.4.48" + }, + "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-13T14:36:38+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/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-09-25T14:11:13+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-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\\EventDispatcher\\": "" + } + }, + "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 dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-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/finder", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "63741784cd7b9967975eec610b256eed3ede022b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", + "reference": "63741784cd7b9967975eec610b256eed3ede022b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/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-09-28T13:32:08+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.32.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.32.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.32.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.32.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.32.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.32.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.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "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.32.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-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.32.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.32.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.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "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.32.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": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "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\\Php81\\": "" + }, + "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 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.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/stopwatch", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fb2c199cf302eb207f8c23e7ee174c1c31a5c004", + "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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 a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/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-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": "symfony/yaml", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.3" + }, + "require-dev": { + "symfony/console": "^5.3|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/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-09-25T14:11:13+00:00" + }, + { + "name": "szepeviktor/phpstan-wordpress", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/szepeviktor/phpstan-wordpress.git", + "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/963887b04c21fe7ac78e61c1351f8b00fff9f8f8", + "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8", + "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.2" + }, + "time": "2025-02-12T18:43:37+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+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-cli/cache-command", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/cache-command.git", + "reference": "14f76b0bc8f9fa0a680e9c70e18fcf627774d055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/cache-command/zipball/14f76b0bc8f9fa0a680e9c70e18fcf627774d055", + "reference": "14f76b0bc8f9fa0a680e9c70e18fcf627774d055", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "cache", + "cache add", + "cache decr", + "cache delete", + "cache flush", + "cache flush-group", + "cache get", + "cache incr", + "cache patch", + "cache pluck", + "cache replace", + "cache set", + "cache supports", + "cache type", + "transient", + "transient delete", + "transient get", + "transient list", + "transient patch", + "transient pluck", + "transient set", + "transient type" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "cache-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Manages object and transient caches.", + "homepage": "https://github.com/wp-cli/cache-command", + "support": { + "issues": "https://github.com/wp-cli/cache-command/issues", + "source": "https://github.com/wp-cli/cache-command/tree/v2.2.0" + }, + "time": "2025-05-06T01:43:20+00:00" + }, + { + "name": "wp-cli/checksum-command", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/checksum-command.git", + "reference": "39992dbd66835f8d5c2cc5bfeacf9d2c450cbafe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/checksum-command/zipball/39992dbd66835f8d5c2cc5bfeacf9d2c450cbafe", + "reference": "39992dbd66835f8d5c2cc5bfeacf9d2c450cbafe", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "core verify-checksums", + "plugin verify-checksums" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "checksum-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Verifies file integrity by comparing to published checksums.", + "homepage": "https://github.com/wp-cli/checksum-command", + "support": { + "issues": "https://github.com/wp-cli/checksum-command/issues", + "source": "https://github.com/wp-cli/checksum-command/tree/v2.3.1" + }, + "time": "2025-04-10T11:02:20+00:00" + }, + { + "name": "wp-cli/config-command", + "version": "v2.3.8", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/config-command.git", + "reference": "994b3dc9e8284fc978366920d5c5ae0dde3a004e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/config-command/zipball/994b3dc9e8284fc978366920d5c5ae0dde3a004e", + "reference": "994b3dc9e8284fc978366920d5c5ae0dde3a004e", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12", + "wp-cli/wp-config-transformer": "^1.4.0" + }, + "require-dev": { + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4.2.8" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "config", + "config edit", + "config delete", + "config create", + "config get", + "config has", + "config is-true", + "config list", + "config path", + "config set", + "config shuffle-salts" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "config-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + }, + { + "name": "Alain Schlesser", + "email": "alain.schlesser@gmail.com", + "homepage": "https://www.alainschlesser.com" + } + ], + "description": "Generates and reads the wp-config.php file.", + "homepage": "https://github.com/wp-cli/config-command", + "support": { + "issues": "https://github.com/wp-cli/config-command/issues", + "source": "https://github.com/wp-cli/config-command/tree/v2.3.8" + }, + "time": "2025-04-11T09:37:43+00:00" + }, + { + "name": "wp-cli/core-command", + "version": "v2.1.20", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/core-command.git", + "reference": "83e4692784a815bb7f5df10b72204f237b5224b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/core-command/zipball/83e4692784a815bb7f5df10b72204f237b5224b9", + "reference": "83e4692784a815bb7f5df10b72204f237b5224b9", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4 || ^2 || ^3", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/checksum-command": "^1 || ^2", + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "core", + "core check-update", + "core download", + "core install", + "core is-installed", + "core multisite-convert", + "core multisite-install", + "core update", + "core update-db", + "core version" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "core-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Downloads, installs, updates, and manages a WordPress installation.", + "homepage": "https://github.com/wp-cli/core-command", + "support": { + "issues": "https://github.com/wp-cli/core-command/issues", + "source": "https://github.com/wp-cli/core-command/tree/v2.1.20" + }, + "time": "2025-04-16T11:23:00+00:00" + }, + { + "name": "wp-cli/cron-command", + "version": "v2.3.2", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/cron-command.git", + "reference": "6f450028a75ebd275f12cad62959a0709bf3e7c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/cron-command/zipball/6f450028a75ebd275f12cad62959a0709bf3e7c1", + "reference": "6f450028a75ebd275f12cad62959a0709bf3e7c1", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/eval-command": "^2.0", + "wp-cli/server-command": "^2.0", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "cron", + "cron test", + "cron event", + "cron event delete", + "cron event list", + "cron event run", + "cron event schedule", + "cron schedule", + "cron schedule list", + "cron event unschedule" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "cron-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Tests, runs, and deletes WP-Cron events; manages WP-Cron schedules.", + "homepage": "https://github.com/wp-cli/cron-command", + "support": { + "issues": "https://github.com/wp-cli/cron-command/issues", + "source": "https://github.com/wp-cli/cron-command/tree/v2.3.2" + }, + "time": "2025-04-02T11:55:20+00:00" + }, + { + "name": "wp-cli/db-command", + "version": "v2.1.3", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/db-command.git", + "reference": "f857c91454d7092fa672bc388512a51752d9264a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/db-command/zipball/f857c91454d7092fa672bc388512a51752d9264a", + "reference": "f857c91454d7092fa672bc388512a51752d9264a", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "db", + "db clean", + "db create", + "db drop", + "db reset", + "db check", + "db optimize", + "db prefix", + "db repair", + "db cli", + "db query", + "db export", + "db import", + "db search", + "db tables", + "db size", + "db columns" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "db-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Performs basic database operations using credentials stored in wp-config.php.", + "homepage": "https://github.com/wp-cli/db-command", + "support": { + "issues": "https://github.com/wp-cli/db-command/issues", + "source": "https://github.com/wp-cli/db-command/tree/v2.1.3" + }, + "time": "2025-04-10T11:02:04+00:00" + }, + { + "name": "wp-cli/embed-command", + "version": "v2.0.18", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/embed-command.git", + "reference": "52f59a1dacf1d4a1c68fd685f27909e1f493816b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/embed-command/zipball/52f59a1dacf1d4a1c68fd685f27909e1f493816b", + "reference": "52f59a1dacf1d4a1c68fd685f27909e1f493816b", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "embed", + "embed fetch", + "embed provider", + "embed provider list", + "embed provider match", + "embed handler", + "embed handler list", + "embed cache", + "embed cache clear", + "embed cache find", + "embed cache trigger" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "embed-command.php" + ], + "psr-4": { + "WP_CLI\\Embeds\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pascal Birchler", + "homepage": "https://pascalbirchler.com/" + } + ], + "description": "Inspects oEmbed providers, clears embed cache, and more.", + "homepage": "https://github.com/wp-cli/embed-command", + "support": { + "issues": "https://github.com/wp-cli/embed-command/issues", + "source": "https://github.com/wp-cli/embed-command/tree/v2.0.18" + }, + "time": "2025-04-10T11:01:32+00:00" + }, + { + "name": "wp-cli/entity-command", + "version": "v2.8.4", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/entity-command.git", + "reference": "213611f8ab619ca137d983e9b987f7fbf1ac21d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/entity-command/zipball/213611f8ab619ca137d983e9b987f7fbf1ac21d4", + "reference": "213611f8ab619ca137d983e9b987f7fbf1ac21d4", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/cache-command": "^1 || ^2", + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/media-command": "^1.1 || ^2", + "wp-cli/super-admin-command": "^1 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "comment", + "comment approve", + "comment count", + "comment create", + "comment delete", + "comment exists", + "comment generate", + "comment get", + "comment list", + "comment meta", + "comment meta add", + "comment meta delete", + "comment meta get", + "comment meta list", + "comment meta patch", + "comment meta pluck", + "comment meta update", + "comment recount", + "comment spam", + "comment status", + "comment trash", + "comment unapprove", + "comment unspam", + "comment untrash", + "comment update", + "menu", + "menu create", + "menu delete", + "menu item", + "menu item add-custom", + "menu item add-post", + "menu item add-term", + "menu item delete", + "menu item list", + "menu item update", + "menu list", + "menu location", + "menu location assign", + "menu location list", + "menu location remove", + "network meta", + "network meta add", + "network meta delete", + "network meta get", + "network meta list", + "network meta patch", + "network meta pluck", + "network meta update", + "option", + "option add", + "option delete", + "option get", + "option list", + "option patch", + "option pluck", + "option update", + "option set-autoload", + "option get-autoload", + "post", + "post create", + "post delete", + "post edit", + "post exists", + "post generate", + "post get", + "post list", + "post meta", + "post meta add", + "post meta clean-duplicates", + "post meta delete", + "post meta get", + "post meta list", + "post meta patch", + "post meta pluck", + "post meta update", + "post term", + "post term add", + "post term list", + "post term remove", + "post term set", + "post update", + "post url-to-id", + "post-type", + "post-type get", + "post-type list", + "site", + "site activate", + "site archive", + "site create", + "site generate", + "site deactivate", + "site delete", + "site empty", + "site list", + "site mature", + "site meta", + "site meta add", + "site meta delete", + "site meta get", + "site meta list", + "site meta patch", + "site meta pluck", + "site meta update", + "site option", + "site private", + "site public", + "site spam", + "site unarchive", + "site unmature", + "site unspam", + "taxonomy", + "taxonomy get", + "taxonomy list", + "term", + "term create", + "term delete", + "term generate", + "term get", + "term list", + "term meta", + "term meta add", + "term meta delete", + "term meta get", + "term meta list", + "term meta patch", + "term meta pluck", + "term meta update", + "term recount", + "term update", + "user", + "user add-cap", + "user add-role", + "user application-password", + "user application-password create", + "user application-password delete", + "user application-password exists", + "user application-password get", + "user application-password list", + "user application-password record-usage", + "user application-password update", + "user create", + "user delete", + "user exists", + "user generate", + "user get", + "user import-csv", + "user list", + "user list-caps", + "user meta", + "user meta add", + "user meta delete", + "user meta get", + "user meta list", + "user meta patch", + "user meta pluck", + "user meta update", + "user remove-cap", + "user remove-role", + "user reset-password", + "user session", + "user session destroy", + "user session list", + "user set-role", + "user signup", + "user signup activate", + "user signup delete", + "user signup get", + "user signup list", + "user spam", + "user term", + "user term add", + "user term list", + "user term remove", + "user term set", + "user unspam", + "user update" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "entity-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Manage WordPress comments, menus, options, posts, sites, terms, and users.", + "homepage": "https://github.com/wp-cli/entity-command", + "support": { + "issues": "https://github.com/wp-cli/entity-command/issues", + "source": "https://github.com/wp-cli/entity-command/tree/v2.8.4" + }, + "time": "2025-05-06T16:12:49+00:00" + }, + { + "name": "wp-cli/eval-command", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/eval-command.git", + "reference": "20ec428a7b9bc604fab0bd33ee8fa20662650455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/eval-command/zipball/20ec428a7b9bc604fab0bd33ee8fa20662650455", + "reference": "20ec428a7b9bc604fab0bd33ee8fa20662650455", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "eval", + "eval-file" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "eval-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Executes arbitrary PHP code or files.", + "homepage": "https://github.com/wp-cli/eval-command", + "support": { + "issues": "https://github.com/wp-cli/eval-command/issues", + "source": "https://github.com/wp-cli/eval-command/tree/v2.2.6" + }, + "time": "2024-11-24T17:28:06+00:00" + }, + { + "name": "wp-cli/export-command", + "version": "v2.1.14", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/export-command.git", + "reference": "2af32bf12c1bccd6561a215dbbafc2f272647ee8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/export-command/zipball/2af32bf12c1bccd6561a215dbbafc2f272647ee8", + "reference": "2af32bf12c1bccd6561a215dbbafc2f272647ee8", + "shasum": "" + }, + "require": { + "nb/oxymel": "~0.1.0", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/import-command": "^1 || ^2", + "wp-cli/media-command": "^1 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "export" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "export-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Exports WordPress content to a WXR file.", + "homepage": "https://github.com/wp-cli/export-command", + "support": { + "issues": "https://github.com/wp-cli/export-command/issues", + "source": "https://github.com/wp-cli/export-command/tree/v2.1.14" + }, + "time": "2025-04-02T15:29:08+00:00" + }, + { + "name": "wp-cli/extension-command", + "version": "v2.1.24", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/extension-command.git", + "reference": "d21a2f504ac43a86b6b08697669b5b0844748133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/extension-command/zipball/d21a2f504ac43a86b6b08697669b5b0844748133", + "reference": "d21a2f504ac43a86b6b08697669b5b0844748133", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4 || ^2 || ^3", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/cache-command": "^2.0", + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/language-command": "^2.0", + "wp-cli/scaffold-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4.3.7" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "plugin", + "plugin activate", + "plugin deactivate", + "plugin delete", + "plugin get", + "plugin install", + "plugin is-installed", + "plugin list", + "plugin path", + "plugin search", + "plugin status", + "plugin toggle", + "plugin uninstall", + "plugin update", + "theme", + "theme activate", + "theme delete", + "theme disable", + "theme enable", + "theme get", + "theme install", + "theme is-installed", + "theme list", + "theme mod", + "theme mod get", + "theme mod set", + "theme mod remove", + "theme path", + "theme search", + "theme status", + "theme update", + "theme mod list" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "extension-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + }, + { + "name": "Alain Schlesser", + "email": "alain.schlesser@gmail.com", + "homepage": "https://www.alainschlesser.com" + } + ], + "description": "Manages plugins and themes, including installs, activations, and updates.", + "homepage": "https://github.com/wp-cli/extension-command", + "support": { + "issues": "https://github.com/wp-cli/extension-command/issues", + "source": "https://github.com/wp-cli/extension-command/tree/v2.1.24" + }, + "time": "2025-05-06T19:17:53+00:00" + }, + { + "name": "wp-cli/i18n-command", + "version": "v2.6.5", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/i18n-command.git", + "reference": "5e73d417398993625331a9f69f6c2ef60f234070" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/i18n-command/zipball/5e73d417398993625331a9f69f6c2ef60f234070", + "reference": "5e73d417398993625331a9f69f6c2ef60f234070", + "shasum": "" + }, + "require": { + "eftec/bladeone": "3.52", + "gettext/gettext": "^4.8", + "mck89/peast": "^1.13.11", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/scaffold-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4.3.9" + }, + "suggest": { + "ext-json": "Used for reading and generating JSON translation files", + "ext-mbstring": "Used for calculating include/exclude matches in code extraction" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "i18n", + "i18n make-pot", + "i18n make-json", + "i18n make-mo", + "i18n make-php", + "i18n update-po" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "i18n-command.php" + ], + "psr-4": { + "WP_CLI\\I18n\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pascal Birchler", + "homepage": "https://pascalbirchler.com/" + } + ], + "description": "Provides internationalization tools for WordPress projects.", + "homepage": "https://github.com/wp-cli/i18n-command", + "support": { + "issues": "https://github.com/wp-cli/i18n-command/issues", + "source": "https://github.com/wp-cli/i18n-command/tree/v2.6.5" + }, + "time": "2025-04-25T21:49:29+00:00" + }, + { + "name": "wp-cli/import-command", + "version": "v2.0.14", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/import-command.git", + "reference": "b2c48f3e51683e825738df62bf8ccc7004c5f0f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/import-command/zipball/b2c48f3e51683e825738df62bf8ccc7004c5f0f9", + "reference": "b2c48f3e51683e825738df62bf8ccc7004c5f0f9", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/export-command": "^1 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "import" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "import-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Imports content from a given WXR file.", + "homepage": "https://github.com/wp-cli/import-command", + "support": { + "issues": "https://github.com/wp-cli/import-command/issues", + "source": "https://github.com/wp-cli/import-command/tree/v2.0.14" + }, + "time": "2025-04-02T16:47:25+00:00" + }, + { + "name": "wp-cli/language-command", + "version": "v2.0.23", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/language-command.git", + "reference": "7221cc39d2b14fd39e55aa7884889f26eec2f822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/language-command/zipball/7221cc39d2b14fd39e55aa7884889f26eec2f822", + "reference": "7221cc39d2b14fd39e55aa7884889f26eec2f822", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "language", + "language core", + "language core activate", + "language core is-installed", + "language core install", + "language core list", + "language core uninstall", + "language core update", + "language plugin", + "language plugin is-installed", + "language plugin install", + "language plugin list", + "language plugin uninstall", + "language plugin update", + "language theme", + "language theme is-installed", + "language theme install", + "language theme list", + "language theme uninstall", + "language theme update", + "site switch-language" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "language-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Installs, activates, and manages language packs.", + "homepage": "https://github.com/wp-cli/language-command", + "support": { + "issues": "https://github.com/wp-cli/language-command/issues", + "source": "https://github.com/wp-cli/language-command/tree/v2.0.23" + }, + "time": "2025-04-10T11:09:04+00:00" + }, + { + "name": "wp-cli/maintenance-mode-command", + "version": "v2.1.3", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/maintenance-mode-command.git", + "reference": "b947e094e00b7b68c6376ec9bd03303515864062" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/maintenance-mode-command/zipball/b947e094e00b7b68c6376ec9bd03303515864062", + "reference": "b947e094e00b7b68c6376ec9bd03303515864062", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "maintenance-mode", + "maintenance-mode activate", + "maintenance-mode deactivate", + "maintenance-mode status", + "maintenance-mode is-active" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "maintenance-mode-command.php" + ], + "psr-4": { + "WP_CLI\\MaintenanceMode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thrijith Thankachan", + "email": "thrijith13@gmail.com", + "homepage": "https://thrijith.com" + } + ], + "description": "Activates, deactivates or checks the status of the maintenance mode of a site.", + "homepage": "https://github.com/wp-cli/maintenance-mode-command", + "support": { + "issues": "https://github.com/wp-cli/maintenance-mode-command/issues", + "source": "https://github.com/wp-cli/maintenance-mode-command/tree/v2.1.3" + }, + "time": "2024-11-24T17:26:30+00:00" + }, + { + "name": "wp-cli/media-command", + "version": "v2.2.2", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/media-command.git", + "reference": "a810ea0e68473fce6a234e67c6c5f19bb820a753" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/media-command/zipball/a810ea0e68473fce6a234e67c6c5f19bb820a753", + "reference": "a810ea0e68473fce6a234e67c6c5f19bb820a753", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/extension-command": "^2.0", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "media", + "media import", + "media regenerate", + "media image-size" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "media-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Imports files as attachments, regenerates thumbnails, or lists registered image sizes.", + "homepage": "https://github.com/wp-cli/media-command", + "support": { + "issues": "https://github.com/wp-cli/media-command/issues", + "source": "https://github.com/wp-cli/media-command/tree/v2.2.2" + }, + "time": "2025-04-11T09:28:29+00:00" + }, + { + "name": "wp-cli/mustache", + "version": "v2.14.99", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/mustache.php.git", + "reference": "ca23b97ac35fbe01c160549eb634396183d04a59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/mustache.php/zipball/ca23b97ac35fbe01c160549eb634396183d04a59", + "reference": "ca23b97ac35fbe01c160549eb634396183d04a59", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "replace": { + "mustache/mustache": "^2.14.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.19.3", + "yoast/phpunit-polyfills": "^2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "support": { + "source": "https://github.com/wp-cli/mustache.php/tree/v2.14.99" + }, + "time": "2025-05-06T16:15:37+00:00" + }, + { + "name": "wp-cli/mustangostang-spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/spyc.git", + "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/spyc/zipball/6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", + "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "includes/functions.php" + ], + "psr-4": { + "Mustangostang\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", + "homepage": "https://github.com/mustangostang/spyc/", + "support": { + "source": "https://github.com/wp-cli/spyc/tree/autoload" + }, + "time": "2017-04-25T11:26:20+00:00" + }, + { + "name": "wp-cli/package-command", + "version": "v2.6.0", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/package-command.git", + "reference": "682d8c6bb30c782c3b09c015478c7cbe1cc727a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/package-command/zipball/682d8c6bb30c782c3b09c015478c7cbe1cc727a9", + "reference": "682d8c6bb30c782c3b09c015478c7cbe1cc727a9", + "shasum": "" + }, + "require": { + "composer/composer": "^2.2.25", + "ext-json": "*", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/scaffold-command": "^1 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "package", + "package browse", + "package install", + "package list", + "package update", + "package uninstall" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "package-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Lists, installs, and removes WP-CLI packages.", + "homepage": "https://github.com/wp-cli/package-command", + "support": { + "issues": "https://github.com/wp-cli/package-command/issues", + "source": "https://github.com/wp-cli/package-command/tree/v2.6.0" + }, + "time": "2025-04-11T09:28:45+00:00" + }, + { + "name": "wp-cli/php-cli-tools", + "version": "v0.12.5", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/php-cli-tools.git", + "reference": "34b83b4f700df8a4ec3fd17bf7e7e7d8ca5f28da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/34b83b4f700df8a4ec3fd17bf7e7e7d8ca5f28da", + "reference": "34b83b4f700df8a4ec3fd17bf7e7e7d8ca5f28da", + "shasum": "" + }, + "require": { + "php": ">= 5.6.0" + }, + "require-dev": { + "roave/security-advisories": "dev-latest", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.11.x-dev" + } + }, + "autoload": { + "files": [ + "lib/cli/cli.php" + ], + "psr-0": { + "cli": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@handbuilt.co", + "role": "Maintainer" + }, + { + "name": "James Logsdon", + "email": "jlogsdon@php.net", + "role": "Developer" + } + ], + "description": "Console utilities for PHP", + "homepage": "http://github.com/wp-cli/php-cli-tools", + "keywords": [ + "cli", + "console" + ], + "support": { + "issues": "https://github.com/wp-cli/php-cli-tools/issues", + "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.12.5" + }, + "time": "2025-03-26T16:13:46+00:00" + }, + { + "name": "wp-cli/process", + "version": "v5.9.99", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/process.git", + "reference": "f0aec5ca26a702d3157e3a19982b662521ac2b81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/process/zipball/f0aec5ca26a702d3157e3a19982b662521ac2b81", + "reference": "f0aec5ca26a702d3157e3a19982b662521ac2b81", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "replace": { + "symfony/process": "^5.4.47" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Symfony Process Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/wp-cli/process/tree/v5.9.99" + }, + "time": "2025-05-06T21:26:50+00:00" + }, + { + "name": "wp-cli/rewrite-command", + "version": "v2.0.15", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/rewrite-command.git", + "reference": "277ec689b7c268680ff429f52558508622c9b34c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/rewrite-command/zipball/277ec689b7c268680ff429f52558508622c9b34c", + "reference": "277ec689b7c268680ff429f52558508622c9b34c", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "rewrite", + "rewrite flush", + "rewrite list", + "rewrite structure" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "rewrite-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Lists or flushes the site's rewrite rules, updates the permalink structure.", + "homepage": "https://github.com/wp-cli/rewrite-command", + "support": { + "issues": "https://github.com/wp-cli/rewrite-command/issues", + "source": "https://github.com/wp-cli/rewrite-command/tree/v2.0.15" + }, + "time": "2025-04-02T12:09:09+00:00" + }, + { + "name": "wp-cli/role-command", + "version": "v2.0.16", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/role-command.git", + "reference": "ed57fb5436b4d47954b07e56c734d19deb4fc491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/role-command/zipball/ed57fb5436b4d47954b07e56c734d19deb4fc491", + "reference": "ed57fb5436b4d47954b07e56c734d19deb4fc491", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "role", + "role create", + "role delete", + "role exists", + "role list", + "role reset", + "cap", + "cap add", + "cap list", + "cap remove" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "role-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Adds, removes, lists, and resets roles and capabilities.", + "homepage": "https://github.com/wp-cli/role-command", + "support": { + "issues": "https://github.com/wp-cli/role-command/issues", + "source": "https://github.com/wp-cli/role-command/tree/v2.0.16" + }, + "time": "2025-04-02T12:24:15+00:00" + }, + { + "name": "wp-cli/scaffold-command", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/scaffold-command.git", + "reference": "b4238ea12e768b3f15d10339a53a8642f82e1d2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/scaffold-command/zipball/b4238ea12e768b3f15d10339a53a8642f82e1d2b", + "reference": "b4238ea12e768b3f15d10339a53a8642f82e1d2b", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "scaffold", + "scaffold underscores", + "scaffold block", + "scaffold child-theme", + "scaffold plugin", + "scaffold plugin-tests", + "scaffold post-type", + "scaffold taxonomy", + "scaffold theme-tests" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "scaffold-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Generates code for post types, taxonomies, blocks, plugins, child themes, etc.", + "homepage": "https://github.com/wp-cli/scaffold-command", + "support": { + "issues": "https://github.com/wp-cli/scaffold-command/issues", + "source": "https://github.com/wp-cli/scaffold-command/tree/v2.5.0" + }, + "time": "2025-04-11T09:29:34+00:00" + }, + { + "name": "wp-cli/search-replace-command", + "version": "v2.1.8", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/search-replace-command.git", + "reference": "65397a7bfdd5ba2cff26f3ab03ef0bcb916c0057" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/search-replace-command/zipball/65397a7bfdd5ba2cff26f3ab03ef0bcb916c0057", + "reference": "65397a7bfdd5ba2cff26f3ab03ef0bcb916c0057", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "search-replace" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "search-replace-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Searches/replaces strings in the database.", + "homepage": "https://github.com/wp-cli/search-replace-command", + "support": { + "issues": "https://github.com/wp-cli/search-replace-command/issues", + "source": "https://github.com/wp-cli/search-replace-command/tree/v2.1.8" + }, + "time": "2025-04-02T13:07:50+00:00" + }, + { + "name": "wp-cli/server-command", + "version": "v2.0.15", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/server-command.git", + "reference": "80a9243f94e0ac073f9bfdb516d2ac7e1fa01a71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/server-command/zipball/80a9243f94e0ac073f9bfdb516d2ac7e1fa01a71", + "reference": "80a9243f94e0ac073f9bfdb516d2ac7e1fa01a71", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "server" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "server-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Launches PHP's built-in web server for a specific WordPress installation.", + "homepage": "https://github.com/wp-cli/server-command", + "support": { + "issues": "https://github.com/wp-cli/server-command/issues", + "source": "https://github.com/wp-cli/server-command/tree/v2.0.15" + }, + "time": "2025-04-10T11:03:13+00:00" + }, + { + "name": "wp-cli/shell-command", + "version": "v2.0.16", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/shell-command.git", + "reference": "3af53a9f4b240e03e77e815b2ee10f229f1aa591" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/shell-command/zipball/3af53a9f4b240e03e77e815b2ee10f229f1aa591", + "reference": "3af53a9f4b240e03e77e815b2ee10f229f1aa591", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "shell" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "shell-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Opens an interactive PHP console for running and testing PHP code.", + "homepage": "https://github.com/wp-cli/shell-command", + "support": { + "issues": "https://github.com/wp-cli/shell-command/issues", + "source": "https://github.com/wp-cli/shell-command/tree/v2.0.16" + }, + "time": "2025-04-11T09:39:33+00:00" + }, + { + "name": "wp-cli/super-admin-command", + "version": "v2.0.16", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/super-admin-command.git", + "reference": "54ac063c384743ee414806d42cb8c61c6aa1fa8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/super-admin-command/zipball/54ac063c384743ee414806d42cb8c61c6aa1fa8e", + "reference": "54ac063c384743ee414806d42cb8c61c6aa1fa8e", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/entity-command": "^1.3 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "super-admin", + "super-admin add", + "super-admin list", + "super-admin remove" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "super-admin-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Lists, adds, or removes super admin users on a multisite installation.", + "homepage": "https://github.com/wp-cli/super-admin-command", + "support": { + "issues": "https://github.com/wp-cli/super-admin-command/issues", + "source": "https://github.com/wp-cli/super-admin-command/tree/v2.0.16" + }, + "time": "2025-04-02T13:07:32+00:00" + }, + { + "name": "wp-cli/widget-command", + "version": "v2.1.12", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/widget-command.git", + "reference": "73084053f7b32d92583e44d870b81f287beea6a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/widget-command/zipball/73084053f7b32d92583e44d870b81f287beea6a9", + "reference": "73084053f7b32d92583e44d870b81f287beea6a9", + "shasum": "" + }, + "require": { + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "wp-cli/extension-command": "^1.2 || ^2", + "wp-cli/wp-cli-tests": "^4" + }, + "type": "wp-cli-package", + "extra": { + "bundled": true, + "commands": [ + "widget", + "widget add", + "widget deactivate", + "widget delete", + "widget list", + "widget move", + "widget reset", + "widget update", + "sidebar", + "sidebar list" + ], + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "files": [ + "widget-command.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Bachhuber", + "email": "daniel@runcommand.io", + "homepage": "https://runcommand.io" + } + ], + "description": "Adds, moves, and removes widgets; lists sidebars.", + "homepage": "https://github.com/wp-cli/widget-command", + "support": { + "issues": "https://github.com/wp-cli/widget-command/issues", + "source": "https://github.com/wp-cli/widget-command/tree/v2.1.12" + }, + "time": "2025-04-11T09:29:37+00:00" + }, + { + "name": "wp-cli/wp-cli", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/wp-cli.git", + "reference": "03d30d4138d12b4bffd8b507b82e56e129e0523f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/03d30d4138d12b4bffd8b507b82e56e129e0523f", + "reference": "03d30d4138d12b4bffd8b507b82e56e129e0523f", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": "^5.6 || ^7.0 || ^8.0", + "symfony/finder": ">2.7", + "wp-cli/mustache": "^2.14.99", + "wp-cli/mustangostang-spyc": "^0.6.3", + "wp-cli/php-cli-tools": "~0.12.4" + }, + "require-dev": { + "wp-cli/db-command": "^1.3 || ^2", + "wp-cli/entity-command": "^1.2 || ^2", + "wp-cli/extension-command": "^1.1 || ^2", + "wp-cli/package-command": "^1 || ^2", + "wp-cli/wp-cli-tests": "^4.3.10" + }, + "suggest": { + "ext-readline": "Include for a better --prompt implementation", + "ext-zip": "Needed to support extraction of ZIP archives when doing downloads or updates" + }, + "bin": [ + "bin/wp", + "bin/wp.bat" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.12.x-dev" + } + }, + "autoload": { + "psr-0": { + "WP_CLI\\": "php/" + }, + "classmap": [ + "php/class-wp-cli.php", + "php/class-wp-cli-command.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI framework", + "homepage": "https://wp-cli.org", + "keywords": [ + "cli", + "wordpress" + ], + "support": { + "docs": "https://make.wordpress.org/cli/handbook/", + "issues": "https://github.com/wp-cli/wp-cli/issues", + "source": "https://github.com/wp-cli/wp-cli" + }, + "time": "2025-05-07T01:16:12+00:00" + }, + { + "name": "wp-cli/wp-cli-bundle", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/wp-cli-bundle.git", + "reference": "d639a3dab65f4b935b21c61ea3662bf3258a03a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/wp-cli-bundle/zipball/d639a3dab65f4b935b21c61ea3662bf3258a03a5", + "reference": "d639a3dab65f4b935b21c61ea3662bf3258a03a5", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "wp-cli/cache-command": "^2", + "wp-cli/checksum-command": "^2.1", + "wp-cli/config-command": "^2.1", + "wp-cli/core-command": "^2.1", + "wp-cli/cron-command": "^2", + "wp-cli/db-command": "^2", + "wp-cli/embed-command": "^2", + "wp-cli/entity-command": "^2", + "wp-cli/eval-command": "^2", + "wp-cli/export-command": "^2", + "wp-cli/extension-command": "^2.1", + "wp-cli/i18n-command": "^2", + "wp-cli/import-command": "^2", + "wp-cli/language-command": "^2", + "wp-cli/maintenance-mode-command": "^2", + "wp-cli/media-command": "^2", + "wp-cli/package-command": "^2.1", + "wp-cli/process": "5.9.99", + "wp-cli/rewrite-command": "^2", + "wp-cli/role-command": "^2", + "wp-cli/scaffold-command": "^2", + "wp-cli/search-replace-command": "^2", + "wp-cli/server-command": "^2", + "wp-cli/shell-command": "^2", + "wp-cli/super-admin-command": "^2", + "wp-cli/widget-command": "^2", + "wp-cli/wp-cli": "^2.12" + }, + "require-dev": { + "roave/security-advisories": "dev-latest", + "wp-cli/wp-cli-tests": "^4" + }, + "suggest": { + "psy/psysh": "Enhanced `wp shell` functionality" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.12.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI bundle package with default commands.", + "homepage": "https://wp-cli.org", + "keywords": [ + "cli", + "wordpress" + ], + "support": { + "docs": "https://make.wordpress.org/cli/handbook/", + "issues": "https://github.com/wp-cli/wp-cli-bundle/issues", + "source": "https://github.com/wp-cli/wp-cli-bundle" + }, + "time": "2025-05-07T02:15:53+00:00" + }, + { + "name": "wp-cli/wp-config-transformer", + "version": "v1.4.2", + "source": { + "type": "git", + "url": "https://github.com/wp-cli/wp-config-transformer.git", + "reference": "b78cab1159b43eb5ee097e2cfafe5eab573d2a8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-cli/wp-config-transformer/zipball/b78cab1159b43eb5ee097e2cfafe5eab573d2a8a", + "reference": "b78cab1159b43eb5ee097e2cfafe5eab573d2a8a", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/WPConfigTransformer.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frankie Jarrett", + "email": "fjarrett@gmail.com" + } + ], + "description": "Programmatically edit a wp-config.php file.", + "homepage": "https://github.com/wp-cli/wp-config-transformer", + "support": { + "issues": "https://github.com/wp-cli/wp-config-transformer/issues", + "source": "https://github.com/wp-cli/wp-config-transformer/tree/v1.4.2" + }, + "time": "2025-03-31T08:37:05+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-graphql/wp-graphql-testcase", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/wp-graphql/wp-graphql-testcase.git", + "reference": "572d4c51e9a0a33ec1b99970155fe005f468f4ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-graphql/wp-graphql-testcase/zipball/572d4c51e9a0a33ec1b99970155fe005f468f4ec", + "reference": "572d4c51e9a0a33ec1b99970155fe005f468f4ec", + "shasum": "" + }, + "require": { + "php-extended/polyfill-php80-str-utils": "^1.3" + }, + "require-dev": { + "automattic/vipwpcs": "^2.3", + "composer/installers": "^1.9", + "johnpbloch/wordpress": "^6.1", + "php-coveralls/php-coveralls": "2.4.3", + "squizlabs/php_codesniffer": "^3.5", + "wp-coding-standards/wpcs": "^2.3", + "wpackagist-plugin/wp-graphql": "^1.26" + }, + "suggest": { + "codeception/module-asserts": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLTestcase to work.", + "codeception/util-universalframework": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLTestcase to work.", + "guzzlehttp/guzzle": "Needed for \\Tests\\WPGraphQL\\Codeception\\Module\\WPGraphQL to work.", + "lucatume/wp-browser": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLTestcase to work.", + "phpunit/phpunit": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLUnitTestcase to work.", + "wp-phpunit/wp-phpunit": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLUnitTestcase to work.", + "yoast/phpunit-polyfills": "Needed for \\Tests\\WPGraphQL\\TestCase\\WPGraphQLUnitTestcase to work." + }, + "type": "library", + "extra": { + "installer-paths": { + "local/public/wp-content/plugins/{$name}/": [ + "type:wordpress-plugin" + ] + }, + "wordpress-install-dir": "local/public" + }, + "autoload": { + "psr-4": { + "Tests\\WPGraphQL\\": "src/" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Geoff Taylor", + "email": "geoffrey.taylor@outlook.com" + } + ], + "description": "Codeception module for WPGraphQL API testing", + "support": { + "issues": "https://github.com/wp-graphql/wp-graphql-testcase/issues", + "source": "https://github.com/wp-graphql/wp-graphql-testcase/tree/v3.4.0" + }, + "time": "2024-08-08T18:48:14+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": [ + { + "package": "phpcompatibility/php-compatibility", + "version": "dev-develop", + "alias": "9.9.9", + "alias_normalized": "9.9.9.0" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "phpcompatibility/php-compatibility": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.4 || ^8.0" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/plugins/wpgraphql-webhooks/deactivation.php b/plugins/wpgraphql-webhooks/deactivation.php new file mode 100644 index 00000000..615f4d67 --- /dev/null +++ b/plugins/wpgraphql-webhooks/deactivation.php @@ -0,0 +1,20 @@ +name === 'Slow Endpoint' ) { + return 30; + } + return $timeout; +}, 10, 2 ); +``` + +## 7. Filter: `graphql_webhooks_sslverify` + +**Description:** +Filters whether SSL verification should be enabled when sending the webhook HTTP request. Defaults to `true`. + +**Parameters:** +- `$sslverify` (bool): Current SSL verification setting. +- `$webhook` (Webhook): The webhook entity instance. + +**Returns:** +Modified SSL verification setting. + +**Example Usage:** + +```php +add_filter( 'graphql_webhooks_sslverify', function( $sslverify, $webhook ) { + // Disable SSL verification for local development webhook + if ( strpos( $webhook->url, 'localhost' ) !== false ) { + return false; + } + return $sslverify; +}, 10, 2 ); +``` + +## 8. Filter: `graphql_webhooks_test_mode` + +**Description:** +Filters whether the webhook HTTP request should be sent in blocking mode. This is useful for debugging or testing webhook delivery. + +**Parameters:** +- `$test_mode` (bool): Whether test mode is enabled (default: `false`). +- `$webhook` (Webhook): The webhook entity instance. + +**Returns:** +`true` to enable blocking mode, `false` otherwise. + +**Example Usage:** + +```php +add_filter( 'graphql_webhooks_test_mode', function( $test_mode, $webhook ) { + // Enable test mode for a specific webhook + if ( $webhook->name === 'Test Webhook' ) { + return true; + } + return $test_mode; +}, 10, 2 ); +``` + +## 9. Action: `graphql_webhooks_sent` + +**Description:** +Fires after a webhook HTTP request has been sent. Useful for logging, debugging, or triggering additional side effects. + +**Parameters:** +- `$webhook` (Webhook): The webhook entity instance. +- `$payload` (array): The payload sent to the webhook. +- `$response` (WP_HTTP_Response|WP_Error): The response or error returned from the HTTP request. + +**Example Usage:** + +```php +add_action( 'graphql_webhooks_sent', function( $webhook, $payload, $response ) { + if ( is_wp_error( $response ) ) { + error_log( "Webhook '{$webhook->name}' failed: " . $response->get_error_message() ); + } else { + error_log( "Webhook '{$webhook->name}' sent successfully with response code: " . wp_remote_retrieve_response_code( $response ) ); + } +}, 10, 3 ); +``` + +## 10. Action: `graphql_webhooks_before_trigger` + +**Description:** +Fires before webhooks are triggered for a specific event. Useful for modifying payload or short-circuiting webhook triggers. + +**Parameters:** +- `$event` (string): The event key being triggered. +- `$payload` (array): The payload data for the event. + +**Example Usage:** +```php +add_action( 'graphql_webhooks_before_trigger', function( $event, &$payload ) { + if ( $event === 'post_published' ) { + // Add extra data before triggering webhooks + $payload['extra_info'] = 'Additional context'; + } +}, 10, 2 ); +``` + +## 11. Action: `graphql_webhooks_after_trigger` + +**Description:** +Fires after webhooks have been triggered for a specific event. Useful for cleanup or logging. + +**Parameters:** +- `$event` (string): The event key that was triggered. +- `$payload` (array): The payload data that was sent. + +**Example Usage:** +```php +add_action( 'graphql_webhooks_after_trigger', function( $event, $payload ) { + error_log( "Completed triggering webhooks for event: $event" ); +}, 10, 2 ); +``` \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/docs/screenshots/create_webhook-ui.png b/plugins/wpgraphql-webhooks/docs/screenshots/create_webhook-ui.png new file mode 100644 index 00000000..24432262 Binary files /dev/null and b/plugins/wpgraphql-webhooks/docs/screenshots/create_webhook-ui.png differ diff --git a/plugins/wpgraphql-webhooks/phpcs/WebhooksStandard/ruleset.xml b/plugins/wpgraphql-webhooks/phpcs/WebhooksStandard/ruleset.xml new file mode 100644 index 00000000..df56a478 --- /dev/null +++ b/plugins/wpgraphql-webhooks/phpcs/WebhooksStandard/ruleset.xml @@ -0,0 +1,4 @@ + + + HWP custom coding standard. + \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/phpstan.neon.dist b/plugins/wpgraphql-webhooks/phpstan.neon.dist new file mode 100644 index 00000000..0776752b --- /dev/null +++ b/plugins/wpgraphql-webhooks/phpstan.neon.dist @@ -0,0 +1,39 @@ +services: + - + class: WPGraphQL\Webhooks\Rules\ClassConstantVarAnnotationRule + tags: + - phpstan.rules.rule +parameters: + level: 8 + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + checkTooWideReturnTypesInProtectedAndPublicMethods: true + inferPrivatePropertyTypeFromConstructor: true + polluteScopeWithAlwaysIterableForeach: false + polluteScopeWithLoopInitialAssignments: false + reportAlwaysTrueInLastCondition: true + reportStaticMethodSignatures: true + reportWrongPhpDocTypeInVarTag: true + treatPhpDocTypesAsCertain: false + stubFiles: + # Simulate added properties + - phpstan/class-wp-post-type.stub + - phpstan/class-wp-taxonomy.stub + bootstrapFiles: + - phpstan/constants.php + - activation.php + - deactivation.php + - wpgraphql-webhooks.php + - access-functions.php + paths: + - activation.php + - deactivation.php + - wpgraphql-webhooks.php + - access-functions.php + - src/ + scanFiles: + - vendor/axepress/wp-graphql-stubs/wp-graphql-stubs.php + excludePaths: + analyseAndScan: + - vendor/* \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/phpstan/Rules/ClassConstantVarAnnotationRule.php b/plugins/wpgraphql-webhooks/phpstan/Rules/ClassConstantVarAnnotationRule.php new file mode 100644 index 00000000..aaeecda3 --- /dev/null +++ b/plugins/wpgraphql-webhooks/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 []; + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/phpstan/class-wp-post-type.stub b/plugins/wpgraphql-webhooks/phpstan/class-wp-post-type.stub new file mode 100644 index 00000000..6f3bdc1c --- /dev/null +++ b/plugins/wpgraphql-webhooks/phpstan/class-wp-post-type.stub @@ -0,0 +1,9 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/readme.txt b/plugins/wpgraphql-webhooks/readme.txt new file mode 100644 index 00000000..86fe536a --- /dev/null +++ b/plugins/wpgraphql-webhooks/readme.txt @@ -0,0 +1,16 @@ +=== WPGraphQL Webhooks === +Contributors: wpengine +Tags: GraphQL, Gatsby, Headless, WPGraphQL, React, Rest, Webhooks +Requires at least: 6.0 +Tested up to: 6.8.1 +Requires PHP: 7.4 +Requires WPGraphQL: 1.8.0 +Stable tag: 0.0.1 +License: GPL-3 +License URI: https://www.gnu.org/licenses/gpl-3.0.html + +== Description == +== Upgrade Notice == +== Frequently Asked Questions == +== Screenshots == +== Changelog == diff --git a/plugins/wpgraphql-webhooks/src/Admin/WebhooksAdmin.php b/plugins/wpgraphql-webhooks/src/Admin/WebhooksAdmin.php new file mode 100644 index 00000000..c6ef119b --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Admin/WebhooksAdmin.php @@ -0,0 +1,469 @@ +repository = $repository; + } + + /** + * Initialize the admin functionality. + * + * @return void + */ + public function init(): void { + add_action( 'admin_menu', [ $this, 'add_admin_menu' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); + add_action( 'admin_init', [ $this, 'handle_actions' ] ); + add_action( 'wp_ajax_test_webhook', [ $this, 'ajax_test_webhook' ] ); + } + + /** + * Registers the webhooks submenu under the GraphQL admin menu. + * + * @return void + */ + public function add_admin_menu(): void { + // Add submenu under GraphQL menu using the correct parent slug + add_submenu_page( + 'graphiql-ide', + __( 'GraphQL Webhooks', 'wpgraphql-webhooks' ), + __( 'Webhooks', 'wpgraphql-webhooks' ), + 'manage_options', + self::ADMIN_PAGE_SLUG, + [ $this, 'render_admin_page' ] + ); + } + + /** + * Generates the admin URL for the webhooks page. + * + * @param array $args Optional. Additional query arguments. + * @return string The admin URL. + */ + public function get_admin_url( array $args = [] ): string { + $defaults = [ + 'page' => self::ADMIN_PAGE_SLUG, + ]; + $args = array_merge( $defaults, $args ); + return add_query_arg( $args, admin_url( 'admin.php' ) ); + } + + /** + * Enqueues admin CSS and JS assets for the webhooks UI. + * + * @param string $hook The current admin page hook. + * @return void + */ + public function enqueue_assets( string $hook ): void { + // Only enqueue on our admin page + if ( false === strpos( $hook, self::ADMIN_PAGE_SLUG ) ) { + return; + } + + wp_enqueue_style( + 'graphql-webhooks-admin', + WPGRAPHQL_WEBHOOKS_PLUGIN_URL . 'assets/css/admin.css', + [], + WPGRAPHQL_WEBHOOKS_VERSION + ); + + wp_enqueue_script( + 'graphql-webhooks-admin', + WPGRAPHQL_WEBHOOKS_PLUGIN_URL . 'assets/js/admin.js', + [ 'jquery' ], + WPGRAPHQL_WEBHOOKS_VERSION, + true + ); + + wp_localize_script( + 'graphql-webhooks-admin', + 'wpGraphQLWebhooks', + [ + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'restUrl' => rest_url( 'graphql-webhooks/v1/' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + 'confirmDelete' => __( 'Are you sure you want to delete this webhook?', 'wpgraphql-webhooks' ), + 'headerTemplate' => $this->get_header_row_template(), + ] + ); + } + + /** + * Returns the HTML template for the webhook header row (for JS rendering). + * + * @return string HTML template. + */ + private function get_header_row_template(): string { + ob_start(); + include __DIR__ . '/views/partials/webhook-header-row.php'; + return ob_get_clean(); + } + + + /** + * Checks if the current user has permission to manage options. + * + * @return bool True if user has permission, false otherwise. + */ + private function verify_admin_permission(): bool { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpgraphql-webhooks' ) ); + return false; + } + return true; + } + + /** + * Verifies a nonce for security. + * + * @param string $nonce_name Nonce field name. + * @param string $action Nonce action. + * @return bool True if nonce is valid, false otherwise. + */ + private function verify_nonce( string $nonce_name, string $action ): bool { + if ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $_REQUEST[ $nonce_name ], $action ) ) { + wp_die( __( 'Security check failed.', 'wpgraphql-webhooks' ) ); + return false; + } + return true; + } + + /** + * Handles saving of a webhook (add or update). + * + * @return void + */ + public function handle_webhook_save() { + if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'webhook_nonce', 'webhook_save' ) ) { + wp_die( __( 'Unauthorized', 'wp-graphql-webhooks' ) ); + } + + $webhook_id = isset( $_POST['webhook_id'] ) ? intval( $_POST['webhook_id'] ) : 0; + if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'webhook_nonce', 'webhook_save' ) ) { + wp_die( __( 'Unauthorized', 'wp-graphql-webhooks' ) ); + } + + $webhook_id = isset( $_POST['webhook_id'] ) ? intval( $_POST['webhook_id'] ) : 0; + $webhook = new Webhook( + $webhook_id, + sanitize_text_field( $_POST['webhook_name'] ?? '' ), + sanitize_text_field( $_POST['webhook_event'] ?? '' ), + esc_url_raw( $_POST['webhook_url'] ?? '' ), + sanitize_text_field( $_POST['webhook_method'] ?? 'POST' ), + $this->sanitize_headers( $_POST['webhook_headers'] ?? [] ) + ); + + $validation = $this->repository->validate( $webhook ); + if ( is_wp_error( $validation ) ) { + wp_die( $validation->get_error_message() ); + } + + if ( $webhook_id > 0 ) { + $result = $this->repository->update( $webhook_id, $webhook ); + $redirect_args = $result ? [ 'updated' => 1 ] : [ 'error' => 1 ]; + } else { + $result = $this->repository->create( $webhook ); + $redirect_args = $result ? [ 'added' => 1 ] : [ 'error' => 1 ]; + } + + + wp_redirect( add_query_arg( $redirect_args, $this->get_admin_url() ) ); + exit; + } + + /** + * Handles admin actions from the webhooks page. + * + * @return void + */ + public function handle_actions(): void { + if ( ! isset( $_REQUEST['page'] ) || self::ADMIN_PAGE_SLUG !== $_REQUEST['page'] ) { + return; + } + + // Handle save action + if ( isset( $_POST['action'] ) && 'save_webhook' === $_POST['action'] ) { + $this->handle_webhook_save(); + } + + // Handle delete action + if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] && isset( $_GET['webhook'] ) ) { + $this->handle_webhook_delete(); + } + } + + /** + * Handles single webhook deletion. + * + * @return void + */ + private function handle_webhook_delete(): void { + // Verify permissions + if ( ! $this->verify_admin_permission() ) { + return; + } + + // Get webhook ID + $webhook_id = intval( $_GET['webhook'] ); + $nonce = isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : ''; + + // Verify nonce + if ( ! wp_verify_nonce( $nonce, 'delete-webhook-' . $webhook_id ) ) { + wp_die( __( 'Security check failed.', 'wpgraphql-webhooks' ) ); + } + + // Delete webhook + $deleted = $this->repository->delete( $webhook_id ) ? 1 : 0; + + // Redirect with result + wp_redirect( add_query_arg( [ 'deleted' => $deleted ], remove_query_arg( [ 'action', 'webhook', '_wpnonce' ], $this->get_admin_url() ) ) ); + exit; + } + + + /** + * Renders the webhooks admin page. + * + * @return void + */ + public function render_admin_page() { + $action = isset( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : 'list'; + switch ( $action ) { + case 'add': + case 'edit': + $this->render_form_page( $action ); + break; + default: + $this->render_list_page(); + break; + } + } + + /** + * Renders the list page using WP_List_Table. + * + * @return void + */ + private function render_list_page() { + require_once __DIR__ . '/WebhooksListTable.php'; + $list_table = new WebhooksListTable( $this->repository ); + include __DIR__ . '/views/webhooks-list.php'; + } + + /** + * Renders the form page for adding or editing a webhook. + * + * @param string $action The action (add or edit). + * @return void + */ + private function render_form_page( $action ) { + $webhook = null; + $form_title = 'add' === $action ? __( 'Add New Webhook', 'wp-graphql-webhooks' ) : __( 'Edit Webhook', 'wp-graphql-webhooks' ); + $submit_text = 'add' === $action ? __( 'Add Webhook', 'wp-graphql-webhooks' ) : __( 'Update Webhook', 'wp-graphql-webhooks' ); + + $name = ''; + $event = ''; + $url = ''; + $method = 'POST'; + $headers = []; + + if ( 'edit' === $action ) { + $webhook_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0; + $webhook = $this->repository->get( $webhook_id ); + + if ( ! $webhook ) { + wp_die( __( 'Webhook not found.', 'wp-graphql-webhooks' ) ); + } + + $name = $webhook->name; + $event = $webhook->event; + $url = $webhook->url; + $method = $webhook->method; + $headers = $webhook->headers; + } + + $events = $this->repository->get_allowed_events(); + $methods = $this->repository->get_allowed_methods(); + $admin = $this; + + include __DIR__ . '/views/webhook-form.php'; + } + + /** + * Sanitizes webhook headers from the form input. + * + * @param array $headers Headers to sanitize. + * @return array Sanitized headers. + */ + private function sanitize_headers( array $headers ): array { + $sanitized_headers = []; + + if ( isset( $headers['name'] ) && isset( $headers['value'] ) ) { + $names = (array) $headers['name']; + $values = (array) $headers['value']; + + foreach ( $names as $index => $name ) { + $name = sanitize_text_field( $name ); + $value = sanitize_text_field( $values[ $index ] ?? '' ); + + if ( ! empty( $name ) && ! empty( $value ) ) { + $sanitized_headers[ $name ] = $value; + } + } + } + + return $sanitized_headers; + } + + /** + * Handles AJAX requests to test a webhook. + * + * @return void + */ + public function ajax_test_webhook(): void { + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'wp_rest' ) ) { + wp_send_json_error( [ + 'message' => __( 'Invalid security token.', 'wpgraphql-webhooks' ), + 'error_code' => 'invalid_nonce' + ] ); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ + 'message' => __( 'You do not have permission to test webhooks.', 'wpgraphql-webhooks' ), + 'error_code' => 'insufficient_permissions' + ] ); + } + + $webhook_id = isset( $_POST['webhook_id'] ) ? intval( $_POST['webhook_id'] ) : 0; + if ( ! $webhook_id ) { + wp_send_json_error( [ + 'message' => __( 'Invalid webhook ID.', 'wpgraphql-webhooks' ), + 'error_code' => 'invalid_webhook_id' + ] ); + } + + $webhook = $this->repository->get( $webhook_id ); + if ( ! $webhook ) { + wp_send_json_error( [ + 'message' => __( 'Webhook not found.', 'wpgraphql-webhooks' ), + 'error_code' => 'webhook_not_found' + ] ); + } + + $test_payload = [ + 'event' => 'test_webhook', + 'timestamp' => current_time( 'mysql' ), + 'webhook' => [ + 'id' => $webhook->id, + 'name' => $webhook->name, + 'url' => $webhook->url, + ], + 'test_data' => [ + 'message' => 'This is a test webhook dispatch', + 'random' => wp_generate_password( 12, false ), + ], + ]; + + $args = [ + 'method' => $webhook->method, + 'timeout' => 15, + 'redirection' => 5, + 'httpversion' => '1.1', + 'blocking' => true, + 'headers' => array_merge( + [ + 'Content-Type' => 'application/json', + 'User-Agent' => 'WPGraphQL-Webhooks/' . WPGRAPHQL_HEADLESS_WEBHOOKS_VERSION, + ], + $webhook->headers + ), + 'body' => wp_json_encode( $test_payload ), + 'sslverify' => apply_filters( 'graphql_webhooks_sslverify', true ), + ]; + + $args['headers']['X-WPGraphQL-Webhook-Event'] = 'test_webhook'; + $args['headers']['X-WPGraphQL-Webhook-ID'] = (string) $webhook->id; + + $start_time = microtime( true ); + $response = wp_remote_request( $webhook->url, $args ); + $duration_ms = round( ( microtime( true ) - $start_time ) * 1000, 2 ); + + if ( is_wp_error( $response ) ) { + wp_send_json_error( [ + 'message' => sprintf( + __( 'Failed to send test webhook: %s', 'wpgraphql-webhooks' ), + $response->get_error_message() + ), + 'error_code' => $response->get_error_code(), + 'error_data' => $response->get_error_data(), + ] ); + } + + $response_code = wp_remote_retrieve_response_code( $response ); + $response_body = wp_remote_retrieve_body( $response ); + + $is_success = $response_code >= 200 && $response_code < 300; + + $response_data = [ + 'success' => $is_success, + 'message' => $is_success + ? sprintf( __( 'Test webhook sent successfully to %s', 'wpgraphql-webhooks' ), $webhook->url ) + : sprintf( __( 'Webhook returned HTTP %d', 'wpgraphql-webhooks' ), $response_code ), + 'webhook_id' => $webhook->id, + 'webhook_name' => $webhook->name, + 'target_url' => $webhook->url, + 'method' => $webhook->method, + 'response_code' => $response_code, + 'duration_ms' => $duration_ms, + 'timestamp' => current_time( 'c' ), + 'test_payload' => $test_payload, + ]; + + if ( ! empty( $response_body ) ) { + $response_data['response_body'] = strlen( $response_body ) > 1000 + ? substr( $response_body, 0, 1000 ) . '...' + : $response_body; + } + + wp_send_json_success( $response_data ); + } +} diff --git a/plugins/wpgraphql-webhooks/src/Admin/WebhooksListTable.php b/plugins/wpgraphql-webhooks/src/Admin/WebhooksListTable.php new file mode 100644 index 00000000..7af908c3 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Admin/WebhooksListTable.php @@ -0,0 +1,275 @@ +repository = $repository; + + parent::__construct( [ + 'singular' => __( 'Webhook', 'wp-graphql-webhooks' ), + 'plural' => __( 'Webhooks', 'wp-graphql-webhooks' ), + 'ajax' => false, + ] ); + } + + /** + * Get columns + * + * @return array + */ + public function get_columns() { + return [ + 'cb' => '', + 'name' => __( 'Name', 'wp-graphql-webhooks' ), + 'event' => __( 'Event', 'wp-graphql-webhooks' ), + 'method' => __( 'Method', 'wp-graphql-webhooks' ), + 'url' => __( 'URL', 'wp-graphql-webhooks' ), + 'headers' => __( 'Headers', 'wp-graphql-webhooks' ), + ]; + } + + /** + * Get sortable columns + * + * @return array + */ + public function get_sortable_columns() { + return [ + 'name' => [ 'name', false ], + 'event' => [ 'event', false ], + 'method' => [ 'method', false ], + ]; + } + + /** + * Get bulk actions + * + * @return array + */ + public function get_bulk_actions() { + return [ + 'delete' => __( 'Delete', 'wp-graphql-webhooks' ), + ]; + } + + + /** + * Process bulk actions + */ + public function process_bulk_action() { + // Only handle delete action + if ( 'delete' !== $this->current_action() ) { + return; + } + + // Verify nonce + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-' . $this->_args['plural'] ) ) { + wp_die( __( 'Security check failed.', 'wp-graphql-webhooks' ) ); + } + + // Check permissions + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpgraphql-webhooks' ) ); + } + + // Get selected webhooks + $webhook_ids = isset( $_REQUEST['webhook'] ) ? array_map( 'intval', (array) $_REQUEST['webhook'] ) : []; + if ( empty( $webhook_ids ) ) { + return; + } + + // Delete webhooks + $deleted = 0; + foreach ( $webhook_ids as $webhook_id ) { + if ( $this->repository->delete( $webhook_id ) ) { + $deleted++; + } + } + + // Redirect with success message + if ( $deleted > 0 ) { + wp_redirect( add_query_arg( [ 'deleted' => $deleted ], remove_query_arg( [ 'action', 'action2', 'webhook', '_wpnonce' ] ) ) ); + exit; + } + } + + /** + * Prepare items for display + */ + public function prepare_items() { + // Process bulk actions first + $this->process_bulk_action(); + + $per_page = $this->get_items_per_page( 'webhooks_per_page', 20 ); + $current_page = $this->get_pagenum(); + + // Get all webhooks + $webhooks = $this->repository->get_all(); + $total_items = count( $webhooks ); + + // Handle sorting + $orderby = ! empty( $_GET['orderby'] ) ? $_GET['orderby'] : 'name'; + $order = ! empty( $_GET['order'] ) ? $_GET['order'] : 'asc'; + + usort( $webhooks, function( $a, $b ) use ( $orderby, $order ) { + $result = 0; + + switch ( $orderby ) { + case 'name': + $result = strcmp( $a->name, $b->name ); + break; + case 'event': + $result = strcmp( $a->event, $b->event ); + break; + case 'method': + $result = strcmp( $a->method, $b->method ); + break; + } + + return ( 'asc' === $order ) ? $result : -$result; + } ); + + // Pagination + $this->items = array_slice( $webhooks, ( $current_page - 1 ) * $per_page, $per_page ); + + $this->set_pagination_args( [ + 'total_items' => $total_items, + 'per_page' => $per_page, + 'total_pages' => ceil( $total_items / $per_page ), + ] ); + + $columns = $this->get_columns(); + $hidden = []; + $sortable = $this->get_sortable_columns(); + + $this->_column_headers = [ $columns, $hidden, $sortable ]; + } + + /** + * Default column renderer + * + * @param object $item Webhook item. + * @param string $column_name Column name. + * @return string + */ + public function column_default( $item, $column_name ) { + switch ( $column_name ) { + case 'name': + return esc_html( $item->name ); + case 'event': + $events = $this->repository->get_allowed_events(); + return esc_html( $events[ $item->event ] ?? $item->event ); + case 'url': + return '' . esc_html( $item->url ) . ''; + case 'method': + return '' . esc_html( $item->method ) . ''; + case 'headers': + $count = is_array( $item->headers ) ? count( $item->headers ) : 0; + return $count > 0 ? sprintf( __( '%d headers', 'wp-graphql-webhooks' ), $count ) : '—'; + default: + return ''; + } + } + + /** + * Checkbox column + * + * @param object $item Webhook item. + * @return string + */ + public function column_cb( $item ) { + return sprintf( + '', + $item->id + ); + } + + /** + * Name column with row actions + * + * @param object $item Webhook item. + * @return string + */ + public function column_name( $item ) { + $edit_url = add_query_arg( [ + 'page' => 'graphql-webhooks', + 'action' => 'edit', + 'id' => $item->id, + ], admin_url( 'admin.php' ) ); + + $delete_url = wp_nonce_url( + add_query_arg( [ + 'page' => 'graphql-webhooks', + 'action' => 'delete', + 'webhook' => $item->id, + ], admin_url( 'admin.php' ) ), + 'delete-webhook-' . $item->id + ); + + $actions = [ + 'edit' => sprintf( '%s', esc_url( $edit_url ), __( 'Edit', 'wp-graphql-webhooks' ) ), + 'test' => sprintf( '%s', $item->id, __( 'Test', 'wp-graphql-webhooks' ) ), + 'delete' => sprintf( '%s', esc_url( $delete_url ), __( 'Delete', 'wp-graphql-webhooks' ) ), + ]; + + return sprintf( + '%s%s', + esc_url( $edit_url ), + esc_html( $item->name ), + $this->row_actions( $actions ) + ); + } + + /** + * Display when no items + */ + public function no_items() { + _e( 'No webhooks found.', 'wp-graphql-webhooks' ); + } + + /** + * Extra controls to be displayed between bulk actions and pagination + * + * @param string $which Top or bottom. + */ + protected function extra_tablenav( $which ) { + if ( 'top' === $which ) { + ?> +
+ +
+ + +
+ + + +
diff --git a/plugins/wpgraphql-webhooks/src/Admin/views/webhook-form.php b/plugins/wpgraphql-webhooks/src/Admin/views/webhook-form.php new file mode 100644 index 00000000..6f7d1ab3 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Admin/views/webhook-form.php @@ -0,0 +1,124 @@ + + +
+

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

+ + + + +

+
+
diff --git a/plugins/wpgraphql-webhooks/src/Admin/views/webhooks-list.php b/plugins/wpgraphql-webhooks/src/Admin/views/webhooks-list.php new file mode 100644 index 00000000..8fdde115 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Admin/views/webhooks-list.php @@ -0,0 +1,64 @@ + + +
+

+ + + +
+ + +
+

+ +

+
+ +
+

+
+ +
+

+
+ + +
+ prepare_items(); + $list_table->display(); + ?> +
+
diff --git a/plugins/wpgraphql-webhooks/src/Autoloader.php b/plugins/wpgraphql-webhooks/src/Autoloader.php new file mode 100644 index 00000000..91ee0dce --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Autoloader.php @@ -0,0 +1,90 @@ + +
+

+ +

+
+ id = $id; + $this->name = $name; + $this->event = $event; + $this->url = $url; + $this->method = $method; + $this->headers = $headers; + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Events/Interfaces/EventManager.php b/plugins/wpgraphql-webhooks/src/Events/Interfaces/EventManager.php new file mode 100644 index 00000000..bc466320 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Events/Interfaces/EventManager.php @@ -0,0 +1,23 @@ + + */ + private static $event_map = [ + // Post Events (lowercase and uppercase variants) + 'post_create' => 'post_published', + 'post_update' => 'post_updated', + 'post_delete' => 'post_deleted', + 'post_updated' => 'post_updated', + 'post_deleted' => 'post_deleted', + 'post_CREATE' => 'post_published', + 'post_UPDATE' => 'post_updated', + 'post_DELETE' => 'post_deleted', + 'post_reassigned_to_user' => 'post_updated', + 'postmeta_changed (meta_key' => 'post_meta_change', // This will match partial string + + // Term Events + 'term_created' => 'term_created', + 'term_updated' => 'term_updated', + 'term_saved' => 'term_updated', + 'term_deleted' => 'term_deleted', + 'term_CREATE' => 'term_created', + 'term_UPDATE' => 'term_updated', + 'term_DELETE' => 'term_deleted', + 'term_relationship_added' => 'term_assigned', + 'term_relationship_deleted' => 'term_unassigned', + + // User Events + 'user_profile_updated' => 'user_updated', + 'user_meta_updated' => 'user_updated', + 'user_deleted' => 'user_deleted', + 'user_reassigned' => 'user_reassigned', + 'user_UPDATE' => 'user_updated', + 'user_DELETE' => 'user_deleted', + + // Menu Events + 'updated_nav_menu' => 'menu_updated', + 'nav_menu_created' => 'menu_created', + 'set_nav_menu_location' => 'menu_updated', + 'menu_meta_updated' => 'menu_updated', + 'nav_menu_item_added' => 'menu_item_created', + 'update_menu_item' => 'menu_item_updated', + 'nav_menu_item_deleted' => 'menu_item_deleted', + 'menu_item_meta_changed' => 'menu_item_updated', + + // Media Events + 'add_attachment' => 'media_uploaded', + 'attachment_edited' => 'media_updated', + 'attachment_deleted' => 'media_deleted', + 'media_UPDATE' => 'media_updated', + 'media_DELETE' => 'media_deleted', + + // Comment Events + 'comment_transition' => 'comment_status', + 'comment_approved' => 'comment_inserted', + 'comment_UPDATE' => 'comment_status', + 'comment_DELETE' => 'comment_status', + + // Cache Events + 'purge all' => 'cache_purged', + + // Node type mappings (for handle_purge_nodes) + 'post' => 'post_updated', + 'term' => 'term_updated', + 'user' => 'user_updated', + 'comment' => 'comment_status', + 'mediaitem' => 'media_updated', + 'menu' => 'menu_updated', + 'menuitem' => 'menu_item_updated', + ]; + + /** + * Get the mapped webhook event key for a given Smart Cache event. + * + * @param string $smart_cache_event + * @return string|null Returns mapped event key or null if no mapping found. + */ + public static function mapEvent( string $smart_cache_event ): ?string { + // First try direct lookup + if ( isset( self::$event_map[ $smart_cache_event ] ) ) { + return self::$event_map[ $smart_cache_event ]; + } + + // Try lowercase version + $lowercase_event = strtolower( $smart_cache_event ); + if ( isset( self::$event_map[ $lowercase_event ] ) ) { + return self::$event_map[ $lowercase_event ]; + } + + // Handle postmeta_changed partial match + if ( strpos( $smart_cache_event, 'postmeta_changed (meta_key' ) === 0 ) { + return 'post_meta_change'; + } + + // Handle list: prefixed events (from purge method calls) + if ( strpos( $smart_cache_event, 'list:' ) === 0 ) { + $type = substr( $smart_cache_event, 5 ); + return self::mapEvent( $type ); // Recursive call to handle the type + } + + // Handle skipped: prefixed events + if ( strpos( $smart_cache_event, 'skipped:' ) === 0 ) { + $type = substr( $smart_cache_event, 8 ); + return self::mapEvent( $type ); // Recursive call to handle the type + } + + return null; + } + + /** + * Get all mapped webhook events. + * + * @return array + */ + public static function getMappedEvents(): array { + return self::$event_map; + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Events/SmartCacheWebhookManager.php b/plugins/wpgraphql-webhooks/src/Events/SmartCacheWebhookManager.php new file mode 100644 index 00000000..40f83901 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Events/SmartCacheWebhookManager.php @@ -0,0 +1,256 @@ +repository = $repository; + $this->handler = $handler; + } + + /** + * Register Smart Cache purge hooks + */ + public function register_hooks(): void { + add_action( 'wpgraphql_cache_purge_nodes', [ $this, 'handle_purge_nodes' ], 10, 2 ); + add_action( 'graphql_purge', [ $this, 'handle_graphql_purge' ], 10, 3 ); + } + + /** + * Handle node purge events from Smart Cache + */ + public function handle_purge_nodes( string $key, array $nodes ): void { + error_log( "[Webhook] handle_purge_nodes - Key: $key, Node count: " . count( $nodes ) ); + + // Handle empty nodes array + if ( empty( $nodes ) ) { + error_log( "[Webhook] handle_purge_nodes - No nodes provided for key: $key" ); + return; + } + + $node_type = $nodes[0]['type'] ?? null; + + if ( empty( $node_type ) ) { + error_log( "[Webhook] handle_purge_nodes - No node type found in first node for key: $key" ); + return; + } + + $event = SmartCacheEventMapper::mapEvent( strtolower( $node_type ) ); + + if ( $event === null ) { + error_log( "[Webhook] handle_purge_nodes - No mapped event found for node type: $node_type" ); + return; + } + + error_log( "[Webhook] handle_purge_nodes - Mapped '$node_type' to event: $event" ); + + $path = $this->get_path_from_key( $key ); + $smart_cache_keys = $this->get_smart_cache_keys( $nodes ); + + $this->trigger_webhooks( $event, [ + 'key' => $key, + 'path' => $path, + 'nodes' => $nodes, + 'smart_cache_keys' => $smart_cache_keys + ] ); + } + + /** + * Handle general purge events from Smart Cache + */ + public function handle_graphql_purge( string $key, string $event, string $graphql_endpoint ): void { + error_log( "[Webhook] handle_graphql_purge - Key: $key, Event: $event, Endpoint: $graphql_endpoint" ); + + // Skip special prefixed keys (they're not actual entity IDs) + if ( strpos( $key, 'skipped:' ) === 0 || strpos( $key, 'list:' ) === 0 ) { + error_log( "[Webhook] Skipping webhook trigger for special key: $key" ); + return; + } + $mapped_event = SmartCacheEventMapper::mapEvent( strtolower( $event ) ); + + if ( $mapped_event === null ) { + error_log( "[Webhook] handle_graphql_purge - No mapped event found for Smart Cache event: $event" ); + return; + } + + error_log( "[Webhook] handle_graphql_purge - Mapped '$event' to event: $mapped_event" ); + + $path = $this->get_path_from_key( $key ); + + $this->trigger_webhooks( $mapped_event, [ + 'key' => $key, + 'path' => $path, + 'graphql_endpoint' => $graphql_endpoint, + 'smart_cache_keys' => [ $key ] + ] ); + } + + /** + * Trigger webhooks with Smart Cache formatted payload + */ + private function trigger_webhooks( string $event, array $payload ): void { + // Event is already mapped, no need to map again + $allowed_events = $this->repository->get_allowed_events(); + + if ( ! array_key_exists( $event, $allowed_events ) ) { + error_log( "[Webhook] Event '$event' is not in allowed events list." ); + return; + } + + // Set uri fallback if smart_cache_keys is empty + if ( empty( $payload['smart_cache_keys'] ) ) { + $payload['uri'] = $payload['path'] ?? ''; + } + + error_log( "[Webhook] Triggering webhooks for event: $event with payload: " . var_export( $payload, true ) ); + + do_action( 'wpgraphql_webhooks_before_trigger', $event, $payload ); + + $webhooks = $this->repository->get_all(); + error_log( "[Webhook] Found " . count( $webhooks ) . " webhooks for event: $event" ); + $triggered_count = 0; + + foreach ( $webhooks as $webhook ) { + if ( $webhook->event === $event ) { + $this->handler->handle( $webhook, $payload ); + $triggered_count++; + } + } + + error_log( "[Webhook] Triggered $triggered_count webhooks for event: $event" ); + + do_action( 'wpgraphql_webhooks_after_trigger', $event, $payload ); + } + + /** + * Extract Smart Cache keys from nodes + */ + private function get_smart_cache_keys( array $nodes ): array { + $keys = []; + + foreach ( $nodes as $node ) { + if ( isset( $node['id'] ) && ! empty( $node['id'] ) ) { + $keys[] = $node['id']; + } elseif ( isset( $node['databaseId'] ) && ! empty( $node['databaseId'] ) ) { + // Fallback to databaseId if id is not available + $keys[] = $node['databaseId']; + } + } + + return array_filter( $keys ); // Remove empty values + } + + /** + * Get the path from the key + * + * Supports all post types, terms, users, and falls back gracefully. + * Handles special prefixed keys like 'skipped:post', 'list:post', etc. + * + * @param string $key The key to get the path from + * + * @return string + */ + public function get_path_from_key( $key ) { + $path = ''; + + if ( empty( $key ) ) { + error_log( "[Webhook] Empty key provided to get_path_from_key" ); + return $path; + } + + // Handle special prefixed keys (skipped:, list:, etc.) + if ( strpos( $key, ':' ) !== false ) { + error_log( "[Webhook] Prefixed key detected: $key - cannot generate path for non-entity keys" ); + return $path; + } + + try { + $node_id = Relay::fromGlobalId( $key ); + } catch (Exception $e) { + error_log( "[Webhook] Failed to decode GraphQL global ID: $key - " . $e->getMessage() ); + return $path; + } + + $node_type = $node_id['type'] ?? null; + $database_id = $node_id['id'] ?? null; + + if ( empty( $node_type ) || empty( $database_id ) ) { + error_log( "[Webhook] Invalid node ID structure for key: $key (type: $node_type, id: $database_id)" ); + return $path; + } + + $permalink = null; + error_log( "[Webhook] Processing key: $key (type: $node_type, database_id: $database_id)" ); + + switch ( $node_type ) { + case 'post': + case 'page': + default: + $post_id = absint( $database_id ); + if ( $post_id > 0 ) { + $post = get_post( $post_id ); + if ( $post && ! is_wp_error( $post ) ) { + $permalink = get_permalink( $post_id ); + error_log( "[Webhook] Generated permalink for post $post_id: $permalink" ); + } else { + error_log( "[Webhook] Post not found or error for ID: $post_id" ); + } + } + break; + + case 'term': + $term_id = absint( $database_id ); + if ( $term_id > 0 ) { + $term = get_term( $term_id ); + if ( $term && ! is_wp_error( $term ) ) { + $permalink = get_term_link( $term_id ); + error_log( "[Webhook] Generated permalink for term $term_id: $permalink" ); + } else { + error_log( "[Webhook] Term not found or error for ID: $term_id" ); + } + } + break; + + case 'user': + $user_id = absint( $database_id ); + if ( $user_id > 0 ) { + $user = get_user_by( 'id', $user_id ); + if ( $user instanceof \WP_User ) { + $permalink = home_url( '/author/' . $user->user_nicename . '/' ); + error_log( "[Webhook] Generated permalink for user $user_id: $permalink" ); + } else { + error_log( "[Webhook] User not found for ID: $user_id" ); + } + } + break; + } + + if ( ! empty( $permalink ) && is_string( $permalink ) && ! is_wp_error( $permalink ) ) { + $parsed_path = parse_url( $permalink, PHP_URL_PATH ); + if ( $parsed_path !== false ) { + $path = $parsed_path; + error_log( "[Webhook] Final path for key $key: $path" ); + } else { + error_log( "[Webhook] Failed to parse URL path from permalink: $permalink" ); + } + } else { + error_log( "[Webhook] No valid permalink generated for key: $key" ); + } + + return $path; + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Events/WebhookEventManager.php b/plugins/wpgraphql-webhooks/src/Events/WebhookEventManager.php new file mode 100644 index 00000000..33e24e9a --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Events/WebhookEventManager.php @@ -0,0 +1,201 @@ +repository = $repository; + $this->handler = $handler; + } + + /** + * Register specific WordPress event hooks. + */ + public function register_hooks(): void { + add_action( 'transition_post_status', [ $this, 'on_transition_post_status' ], 10, 3 ); + add_action( 'post_updated', [ $this, 'on_post_updated' ], 10, 3 ); + add_action( 'deleted_post', [ $this, 'on_deleted_post' ], 10, 2 ); + add_action( 'added_post_meta', [ $this, 'on_post_meta_change' ], 10, 4 ); + add_action( 'created_term', [ $this, 'on_term_created' ], 10, 3 ); + add_action( 'set_object_terms', [ $this, 'on_term_assigned' ], 10, 6 ); + add_action( 'delete_term_relationships', [ $this, 'on_term_unassigned' ], 10, 3 ); + add_action( 'delete_term', [ $this, 'on_term_deleted' ], 10, 4 ); + add_action( 'added_term_meta', [ $this, 'on_term_meta_change' ], 10, 4 ); + add_action( 'user_register', [ $this, 'on_user_created' ], 10, 1 ); + add_action( 'deleted_user', [ $this, 'on_user_deleted' ], 10, 2 ); + add_action( 'add_attachment', [ $this, 'on_media_uploaded' ], 10, 1 ); + add_action( 'edit_attachment', [ $this, 'on_media_updated' ], 10, 1 ); + add_action( 'delete_attachment', [ $this, 'on_media_deleted' ], 10, 1 ); + add_action( 'wp_insert_comment', [ $this, 'on_comment_inserted' ], 10, 2 ); + add_action( 'transition_comment_status', [ $this, 'on_comment_status' ], 10, 3 ); + + // Register test webhook event handler + add_action( 'graphql_webhooks_test_event', [ $this, 'on_test_webhook' ], 10, 2 ); + } + + /** + * Triggers webhooks for a given event if it is allowed. + * + * @param string $event + * @param array $payload + */ + private function trigger_webhooks( string $event, array $payload ): void { + $allowed_events = $this->repository->get_allowed_events(); + if ( ! array_key_exists( $event, $allowed_events ) ) { + error_log( 'Event ' . $event . ' is not allowed. Allowed events: ' . implode( ', ', $allowed_events ) ); + return; + } + + do_action( 'wpgraphql_webhooks_before_trigger', $event, $payload ); + foreach ( $this->repository->get_all() as $webhook ) { + if ( $webhook->event === $event ) { + $this->handler->handle( $webhook, $payload ); + } + } + + do_action( 'wpgraphql_webhooks_after_trigger', $event, $payload ); + } + + /** Event Handlers **/ + + public function on_transition_post_status( $new_status, $old_status, $post ) { + if ( $old_status !== 'publish' && $new_status === 'publish' ) { + $this->trigger_webhooks( 'post_published', [ 'post_id' => $post->ID ] ); + } + } + + public function on_post_updated( $post_ID, $post_after, $post_before ) { + $this->trigger_webhooks( 'post_updated', [ 'post_id' => $post_ID ] ); + + if ( $post_after->post_author !== $post_before->post_author ) { + $this->trigger_webhooks( 'user_assigned', [ + 'post_id' => $post_ID, + 'author_id' => $post_after->post_author, + ] ); + + $this->trigger_webhooks( 'user_reassigned', [ + 'post_id' => $post_ID, + 'old_author_id' => $post_before->post_author, + 'new_author_id' => $post_after->post_author, + ] ); + } + } + + public function on_deleted_post( $post_ID, $post ) { + $this->trigger_webhooks( 'post_deleted', [ 'post_id' => $post_ID ] ); + } + + public function on_post_meta_change( $meta_id, $post_id, $meta_key, $meta_value ) { + $this->trigger_webhooks( 'post_meta_change', [ + 'post_id' => $post_id, + 'meta_key' => $meta_key, + ] ); + } + + public function on_term_created( $term_id, $tt_id, $taxonomy ) { + $this->trigger_webhooks( 'term_created', [ + 'term_id' => $term_id, + 'taxonomy' => $taxonomy, + ] ); + } + + public function on_term_assigned( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { + foreach ( (array) $terms as $term_id ) { + $this->trigger_webhooks( 'term_assigned', [ + 'object_id' => $object_id, + 'term_id' => $term_id, + 'taxonomy' => $taxonomy, + ] ); + } + } + + public function on_term_unassigned( $object_id, $taxonomy, $term_ids ) { + $this->trigger_webhooks( 'term_unassigned', [ + 'object_id' => $object_id, + 'taxonomy' => $taxonomy, + 'term_ids' => $term_ids, + ] ); + } + + public function on_term_deleted( $term, $tt_id, $taxonomy, $deleted_term ) { + $this->trigger_webhooks( 'term_deleted', [ + 'term_id' => $term, + 'taxonomy' => $taxonomy, + ] ); + } + + public function on_term_meta_change( $meta_id, $term_id, $meta_key, $meta_value ) { + $this->trigger_webhooks( 'term_meta_change', [ + 'term_id' => $term_id, + 'meta_key' => $meta_key, + ] ); + } + + public function on_user_created( $user_id ) { + $this->trigger_webhooks( 'user_created', [ 'user_id' => $user_id ] ); + } + + public function on_user_deleted( $user_id, $reassign ) { + $this->trigger_webhooks( 'user_deleted', [ 'user_id' => $user_id ] ); + } + + public function on_media_uploaded( $post_id ) { + $this->trigger_webhooks( 'media_uploaded', [ 'post_id' => $post_id ] ); + } + + public function on_media_updated( $post_id ) { + $this->trigger_webhooks( 'media_updated', [ 'post_id' => $post_id ] ); + } + + public function on_media_deleted( $post_id ) { + $this->trigger_webhooks( 'media_deleted', [ 'post_id' => $post_id ] ); + } + + public function on_comment_inserted( $comment_id, $comment_object ) { + $this->trigger_webhooks( 'comment_inserted', [ 'comment_id' => $comment_id ] ); + } + + public function on_comment_status( $new_status, $old_status, $comment ) { + $this->trigger_webhooks( 'comment_status', [ + 'comment_id' => $comment->comment_ID, + 'new_status' => $new_status, + ] ); + } + + /** + * Handle test webhook event. + * + * @param \WPGraphQL\Webhooks\Entity\Webhook $webhook The webhook being tested. + * @param array $payload The test payload. + */ + public function on_test_webhook( $webhook, $payload = [] ) { + // For test webhooks, we want to send directly to the specific webhook + // Enable test mode for blocking requests during tests + add_filter( 'graphql_webhooks_test_mode', '__return_true' ); + + // Send the webhook with the test payload + $this->handler->handle( $webhook, $payload ); + + // Remove the test mode filter + remove_filter( 'graphql_webhooks_test_mode', '__return_true' ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Fields/RootQuery.php b/plugins/wpgraphql-webhooks/src/Fields/RootQuery.php new file mode 100644 index 00000000..6b515b34 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Fields/RootQuery.php @@ -0,0 +1,59 @@ + [ 'list_of' => 'Webhook' ], + 'description' => __( 'List all registered webhooks.', 'wpgraphql-webhooks' ), + 'resolve' => function() { + $query = new \WP_Query([ + 'post_type' => 'graphql_webhook', + 'posts_per_page' => -1, + 'post_status' => [ 'publish', 'draft', 'private' ], + ]); + + return $query->posts; + }, + ]); + + // Register the "webhook" field (fetch single webhook by ID) + register_graphql_field( 'RootQuery', 'webhook', [ + 'type' => 'Webhook', + 'description' => __( 'Fetch a webhook by ID.', 'wpgraphql-webhooks' ), + 'args' => [ + 'id' => [ + 'type' => Type::nonNull( Type::id() ), + 'description' => __( 'The global ID of the webhook to retrieve.', 'wpgraphql-webhooks' ), + ], + ], + 'resolve' => function( $root, $args ) { + $post = get_post( $args['id'] ); + if ( $post && $post->post_status !== 'trash' ) { + return $post; + } + return null; + }, + ]); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Handlers/Interfaces/Handler.php b/plugins/wpgraphql-webhooks/src/Handlers/Interfaces/Handler.php new file mode 100644 index 00000000..902ae55b --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Handlers/Interfaces/Handler.php @@ -0,0 +1,24 @@ +name} (ID: {$webhook->id})" ); + error_log( "Event: {$webhook->event}" ); + error_log( "Target URL: {$webhook->url}" ); + error_log( "Method: {$webhook->method}" ); + + $args = [ + 'headers' => $webhook->headers ?: [ 'Content-Type' => 'application/json' ], + 'timeout' => apply_filters( 'graphql_webhooks_timeout', 15 ), // Configurable timeout with a default of 15 seconds + 'blocking' => false, + 'sslverify' => apply_filters( 'graphql_webhooks_sslverify', true, $webhook ), + 'user-agent' => 'WPGraphQL-Webhooks/' . ( defined( 'WPGRAPHQL_WEBHOOKS_VERSION' ) ? WPGRAPHQL_WEBHOOKS_VERSION : '1.0.0' ), + ]; + + // Apply payload filter + $payload = apply_filters( 'graphql_webhooks_payload', $payload, $webhook ); + + // Add webhook metadata to payload + $payload['_webhook_meta'] = [ + 'sent_at' => $dispatch_timestamp, + 'webhook_id' => $webhook->id, + 'webhook_name' => $webhook->name, + 'event_type' => $webhook->event, + ]; + + // Handle different HTTP methods + if ( strtoupper( $webhook->method ) === 'GET' ) { + $url = add_query_arg( $payload, $webhook->url ); + $args['method'] = 'GET'; + error_log( "Payload (GET query params): " . wp_json_encode( $payload ) ); + } else { + $url = $webhook->url; + $args['method'] = strtoupper( $webhook->method ); + $args['body'] = wp_json_encode( $payload ); + + // Ensure Content-Type header is set for non-GET requests + if ( empty( $args['headers']['Content-Type'] ) ) { + $args['headers']['Content-Type'] = 'application/json'; + } + + error_log( "Payload ({$args['method']} body): " . $args['body'] ); + error_log( "Payload size: " . strlen( $args['body'] ) . " bytes" ); + } + + // Log headers + error_log( "Headers: " . wp_json_encode( $args['headers'] ) ); + + // For test mode or debugging, optionally use blocking mode + if ( apply_filters( 'graphql_webhooks_test_mode', false, $webhook ) ) { + $args['blocking'] = true; + error_log( "Test mode enabled - using blocking request" ); + } + + error_log( "====================================\n" ); + + // Send the webhook + $start_time = microtime( true ); + $response = wp_remote_request( $url, $args ); + $end_time = microtime( true ); + $duration = round( ( $end_time - $start_time ) * 1000, 2 ); + + // Log response if in blocking mode + if ( $args['blocking'] ) { + if ( is_wp_error( $response ) ) { + error_log( "\n========== WEBHOOK ERROR ==========" ); + error_log( "❌ ERROR: " . $response->get_error_message() ); + error_log( "Duration: {$duration}ms" ); + error_log( "==================================\n" ); + } else { + $response_code = wp_remote_retrieve_response_code( $response ); + $response_body = wp_remote_retrieve_body( $response ); + + error_log( "\n========== WEBHOOK RESPONSE ==========" ); + error_log( ( $response_code >= 200 && $response_code < 300 ? "✅" : "⚠️" ) . " Response Code: {$response_code}" ); + error_log( "Duration: {$duration}ms" ); + error_log( "Response Body: " . ( strlen( $response_body ) > 500 ? substr( $response_body, 0, 500 ) . '...' : $response_body ) ); + error_log( "====================================\n" ); + } + } + + // Trigger action after webhook is sent + do_action( 'wpgraphql_webhooks_sent', $webhook, $payload, $response ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Mutation/CreateWebhook.php b/plugins/wpgraphql-webhooks/src/Mutation/CreateWebhook.php new file mode 100644 index 00000000..a6c8464a --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Mutation/CreateWebhook.php @@ -0,0 +1,110 @@ + [ + 'title' => [ + 'type' => 'String', + 'description' => __( 'The title of the webhook', 'wpgraphql-webhooks' ), + ], + 'content' => [ + 'type' => 'String', + 'description' => __( 'The content/description of the webhook', 'wpgraphql-webhooks' ), + ], + 'eventTrigger' => [ + 'type' => 'String', + 'description' => __( 'The event hook name that triggers this webhook', 'wpgraphql-webhooks' ), + ], + 'enabled' => [ + 'type' => 'Boolean', + 'description' => __( 'Whether the webhook is enabled', 'wpgraphql-webhooks' ), + ], + 'security' => [ + 'type' => 'String', + 'description' => __( 'Security information for the webhook', 'wpgraphql-webhooks' ), + ], + 'handlerClass' => [ + 'type' => 'String', + 'description' => __( 'The handler class used for dispatching', 'wpgraphql-webhooks' ), + ], + 'handlerConfig' => [ + 'type' => 'String', + 'description' => __( 'Configuration for the handler, JSON encoded', 'wpgraphql-webhooks' ), + ], + 'status' => [ + 'type' => 'String', + 'description' => __( 'Post status, e.g. PUBLISH or DRAFT', 'wpgraphql-webhooks' ), + 'defaultValue' => 'publish', + ], + ], + 'outputFields' => [ + 'webhook' => [ + 'type' => 'Webhook', + 'description' => __( 'The created webhook', 'wpgraphql-webhooks' ), + 'resolve' => function ($payload) { + return get_post( $payload['webhookId'] ); + }, + ], + ], + 'mutateAndGetPayload' => function ($input, $context, $info) { + // Check user capabilities + if ( ! current_user_can( 'manage_options' ) ) { + throw new UserError( __( 'You do not have permission to create webhooks.', 'wpgraphql-webhooks' ) ); + } + + // Prepare post data + $post_data = [ + 'post_type' => 'graphql_webhook', + 'post_title' => sanitize_text_field( $input['title'] ?? '' ), + 'post_content' => sanitize_textarea_field( $input['content'] ?? '' ), + 'post_status' => sanitize_text_field( $input['status'] ?? 'publish' ), + ]; + $post_id = wp_insert_post( $post_data ); + + if ( is_wp_error( $post_id ) ) { + throw new UserError( __( 'Failed to create webhook.', 'wpgraphql-webhooks' ) ); + } + if ( isset( $input['eventTrigger'] ) ) { + update_post_meta( $post_id, '_event_trigger', sanitize_text_field( $input['eventTrigger'] ) ); + } + if ( isset( $input['enabled'] ) ) { + update_post_meta( $post_id, '_enabled', (bool) $input['enabled'] ); + } + if ( isset( $input['security'] ) ) { + update_post_meta( $post_id, '_security', sanitize_text_field( $input['security'] ) ); + } + if ( isset( $input['handlerClass'] ) ) { + update_post_meta( $post_id, '_handler_class', sanitize_text_field( $input['handlerClass'] ) ); + } + if ( isset( $input['handlerConfig'] ) ) { + update_post_meta( $post_id, '_handler_config', sanitize_text_field( $input['handlerConfig'] ) ); + } + + return [ + 'webhookId' => $post_id, + ]; + }, + ] ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Mutation/DeleteWebhook.php b/plugins/wpgraphql-webhooks/src/Mutation/DeleteWebhook.php new file mode 100644 index 00000000..8e700885 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Mutation/DeleteWebhook.php @@ -0,0 +1,81 @@ + [ + 'id' => [ + 'type' => 'ID', + 'description' => __( 'The ID of the webhook to delete', 'wpgraphql-webhooks' ), + ], + ], + 'outputFields' => [ + 'deletedWebhookId' => [ + 'type' => 'ID', + 'description' => __( 'The ID of the deleted webhook', 'wpgraphql-webhooks' ), + 'resolve' => function ($payload) { + return $payload['deletedWebhookId'] ?? null; + }, + ], + 'success' => [ + 'type' => 'Boolean', + 'description' => __( 'Whether the webhook was successfully deleted', 'wpgraphql-webhooks' ), + 'resolve' => function ($payload) { + return $payload['success'] ?? false; + }, + ], + ], + 'mutateAndGetPayload' => function ($input, $context, $info) { + // Capability check + if ( ! current_user_can( 'manage_options' ) ) { + throw new UserError( __( 'You do not have permission to delete webhooks.', 'wpgraphql-webhooks' ) ); + } + + if ( empty( $input['id'] ) ) { + throw new UserError( __( 'The ID of the webhook to delete is required.', 'wpgraphql-webhooks' ) ); + } + $post_id = is_numeric( $input['id'] ) ? (int) $input['id'] : 0; + + if ( $post_id <= 0 ) { + throw new UserError( __( 'Invalid webhook ID.', 'wpgraphql-webhooks' ) ); + } + + $post = get_post( $post_id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { + throw new UserError( __( 'Webhook not found.', 'wpgraphql-webhooks' ) ); + } + + // Delete the post (force delete to bypass trash) + $deleted = wp_delete_post( $post_id, true ); + if ( ! $deleted ) { + throw new UserError( __( 'Failed to delete webhook.', 'wpgraphql-webhooks' ) ); + } + + return [ + 'deletedWebhookId' => $post_id, + 'success' => true, + ]; + }, + ] ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Mutation/UpdateWebhook.php b/plugins/wpgraphql-webhooks/src/Mutation/UpdateWebhook.php new file mode 100644 index 00000000..9c9b0ef5 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Mutation/UpdateWebhook.php @@ -0,0 +1,128 @@ + [ + 'id' => [ + 'type' => 'ID', + 'description' => __('The ID of the webhook to update', 'wpgraphql-webhooks'), + ], + 'title' => [ + 'type' => 'String', + 'description' => __('The new title of the webhook', 'wpgraphql-webhooks'), + ], + 'content' => [ + 'type' => 'String', + 'description' => __('The new content/description of the webhook', 'wpgraphql-webhooks'), + ], + 'eventTrigger' => [ + 'type' => 'String', + 'description' => __('The new event hook name that triggers this webhook', 'wpgraphql-webhooks'), + ], + 'enabled' => [ + 'type' => 'Boolean', + 'description' => __('Whether the webhook is enabled', 'wpgraphql-webhooks'), + ], + 'security' => [ + 'type' => 'String', + 'description' => __('Security information for the webhook', 'wpgraphql-webhooks'), + ], + 'handlerClass' => [ + 'type' => 'String', + 'description' => __('The handler class used for dispatching', 'wpgraphql-webhooks'), + ], + 'handlerConfig' => [ + 'type' => 'String', + 'description' => __('Configuration for the handler, JSON encoded', 'wpgraphql-webhooks'), + ], + 'status' => [ + 'type' => 'String', + 'description' => __('Post status, e.g. PUBLISH or DRAFT', 'wpgraphql-webhooks'), + ], + ], + 'outputFields' => [ + 'webhook' => [ + 'type' => 'Webhook', + 'description' => __('The updated webhook', 'wpgraphql-webhooks'), + 'resolve' => function ($payload) { + return get_post($payload['webhookId']); + }, + ], + ], + 'mutateAndGetPayload' => function ($input, $context, $info) { + if (!current_user_can('manage_options')) { + throw new UserError(__('You do not have permission to update webhooks.', 'wpgraphql-webhooks')); + } + + if (empty($input['id'])) { + throw new UserError(__('The ID of the webhook to update is required.', 'wpgraphql-webhooks')); + } + + $post_id = is_numeric($input['id']) ? (int) $input['id'] : 0; + + if ($post_id <= 0) { + throw new UserError(__('Invalid webhook ID.', 'wpgraphql-webhooks')); + } + + $post = get_post($post_id); + if (!$post || $post->post_type !== 'graphql_webhook') { + throw new UserError(__('Webhook not found.', 'wpgraphql-webhooks')); + } + $post_data = ['ID' => $post_id]; + + if (isset($input['title'])) { + $post_data['post_title'] = sanitize_text_field($input['title']); + } + if (isset($input['content'])) { + $post_data['post_content'] = sanitize_textarea_field($input['content']); + } + if (isset($input['status'])) { + $post_data['post_status'] = sanitize_text_field($input['status']); + } + $updated_post_id = wp_update_post($post_data, true); + if (is_wp_error($updated_post_id)) { + throw new UserError(__('Failed to update webhook.', 'wpgraphql-webhooks')); + } + + if (isset($input['eventTrigger'])) { + update_post_meta($post_id, '_event_trigger', sanitize_text_field($input['eventTrigger'])); + } + if (isset($input['enabled'])) { + update_post_meta($post_id, '_enabled', (bool) $input['enabled']); + } + if (isset($input['security'])) { + update_post_meta($post_id, '_security', sanitize_text_field($input['security'])); + } + if (isset($input['handlerClass'])) { + update_post_meta($post_id, '_handler_class', sanitize_text_field($input['handlerClass'])); + } + if (isset($input['handlerConfig'])) { + update_post_meta($post_id, '_handler_config', sanitize_text_field($input['handlerConfig'])); + } + + return ['webhookId' => $post_id]; + }, + ]); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Plugin.php b/plugins/wpgraphql-webhooks/src/Plugin.php new file mode 100644 index 00000000..f9057edb --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Plugin.php @@ -0,0 +1,153 @@ +includes(); + self::$instance->setup(); + } + + /** + * Plugin init action. + * + * @param self $instance + */ + do_action( 'wpgraphql_webhooks_init', self::$instance ); + + return self::$instance; + } + + private function setup(): void { + Helper::set_hook_prefix( 'wpgraphql_webhooks' ); + WebhookPostType::init(); + + $this->services = new PluginServiceLocator(); + + // Register services + $this->services->set( 'repository', function () { + return new WebhookRepository(); + } ); + + $this->services->set( 'handler', function () { + return new WebhookHandler(); + } ); + + $this->services->set( 'event_manager', function () { + $repository = $this->services->get( 'repository' ); + $handler = $this->services->get( 'handler' ); + + if ( class_exists( 'WPGraphQL\SmartCache\Document' ) || defined( 'WPGRAPHQL_SMART_CACHE_VERSION' ) ) { + return new SmartCacheWebhookManager( $repository, $handler ); + } + + return new WebhookEventManager( $repository, $handler ); + } ); + // Initialize event manager and register hooks + $eventManager = $this->services->get( 'event_manager' ); + $eventManager->register_hooks(); + + // Initialize admin UI + if ( is_admin() ) { + $repository = $this->services->get( 'repository' ); + + if ( class_exists( 'WPGraphQL\Webhooks\Admin\WebhooksAdmin' ) ) { + $admin = new \WPGraphQL\Webhooks\Admin\WebhooksAdmin( $repository ); + $admin->init(); + } + } + + // Initialize REST endpoints + add_action( 'rest_api_init', function () { + $repository = $this->services->get( 'repository' ); + + if ( class_exists( 'WPGraphQL\Webhooks\Rest\WebhookTestEndpoint' ) ) { + $testEndpoint = new \WPGraphQL\Webhooks\Rest\WebhookTestEndpoint( $repository ); + $testEndpoint->register_routes(); + } + } ); + } + + /** + * Include required files. + */ + private function includes(): void { + if ( + defined( 'WPGRAPHQL_WEBHOOKS_AUTOLOAD' ) + && false !== WPGRAPHQL_WEBHOOKS_AUTOLOAD + && defined( 'WPGRAPHQL_WEBHOOKS_PLUGIN_DIR' ) + ) { + require_once WPGRAPHQL_WEBHOOKS_PLUGIN_DIR . 'vendor/autoload.php'; + } + } + + /** + * Get the webhook repository instance. + * + * Provides access to the WebhookRepository for managing webhook data. + * + * @return WebhookRepository The repository instance. + */ + public function get_repository(): WebhookRepository { + return $this->services->get( 'repository' ); + } + + /** + * Prevent cloning. + */ + public function __clone() { + _doing_it_wrong( __FUNCTION__, 'The plugin main class should not be cloned.', '0.0.1' ); + } + + /** + * Prevent unserializing. + */ + public function __wakeup(): void { + _doing_it_wrong( __FUNCTION__, 'De-serializing instances of the plugin main class is not allowed.', '0.0.1' ); + } + } + +endif; \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/PostTypes/WebhookPostType.php b/plugins/wpgraphql-webhooks/src/PostTypes/WebhookPostType.php new file mode 100644 index 00000000..bd9fdb74 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/PostTypes/WebhookPostType.php @@ -0,0 +1,73 @@ + __( 'Webhooks', 'wpgraphql-webhooks' ), + 'singular_name' => __( 'Webhook', 'wpgraphql-webhooks' ), + 'add_new' => __( 'Add New', 'wpgraphql-webhooks' ), + 'add_new_item' => __( 'Add New Webhook', 'wpgraphql-webhooks' ), + 'edit_item' => __( 'Edit Webhook', 'wpgraphql-webhooks' ), + 'new_item' => __( 'New Webhook', 'wpgraphql-webhooks' ), + 'view_item' => __( 'View Webhook', 'wpgraphql-webhooks' ), + 'search_items' => __( 'Search Webhooks', 'wpgraphql-webhooks' ), + 'not_found' => __( 'No Webhooks found', 'wpgraphql-webhooks' ), + 'not_found_in_trash' => __( 'No Webhooks found in Trash', 'wpgraphql-webhooks' ), + 'parent_item_colon' => __( 'Parent Webhook:', 'wpgraphql-webhooks' ), + 'menu_name' => __( 'Webhooks', 'wpgraphql-webhooks' ), + ]; + $args = [ + 'labels' => $labels, + 'publicly_queryable' => false, + 'hierarchical' => false, + 'description' => 'Manages GraphQL Webhooks', + 'taxonomies' => [], + 'public' => false, + 'show_ui' => false, + 'show_in_menu' => false, + 'show_in_admin_bar' => false, + 'menu_icon' => 'dashicons-share-alt', + 'show_in_nav_menus' => false, + 'exclude_from_search' => true, + 'has_archive' => false, + 'query_var' => true, + 'can_export' => true, + 'rewrite' => false, + 'capability_type' => 'webhook', + 'supports' => [ 'title' ], + 'capabilities' => [ + 'create_posts' => 'manage_options', + 'edit_posts' => 'manage_options', + 'edit_post' => 'manage_options', + 'delete_posts' => 'manage_options', + 'delete_post' => 'manage_options', + 'read_post' => 'manage_options', + 'read_private_posts' => 'manage_options', + ], + 'map_meta_cap' => false, + ]; + + register_post_type( 'graphql_webhook', $args ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Repository/Interfaces/WebhookRepositoryInterface.php b/plugins/wpgraphql-webhooks/src/Repository/Interfaces/WebhookRepositoryInterface.php new file mode 100644 index 00000000..ad4ed724 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Repository/Interfaces/WebhookRepositoryInterface.php @@ -0,0 +1,71 @@ + + */ + private $default_events = [ + 'post_published' => 'Post Published', + 'post_updated' => 'Post Updated', + 'post_deleted' => 'Post Deleted', + 'post_meta_change' => 'Post Meta Changed', + 'term_created' => 'Term Created', + 'term_assigned' => 'Term Assigned to Post', + 'term_unassigned' => 'Term Unassigned from Post', + 'term_deleted' => 'Term Deleted', + 'term_meta_change' => 'Term Meta Changed', + 'user_created' => 'User Created', + 'user_assigned' => 'User Assigned as Author', + 'user_deleted' => 'User Deleted', + 'user_reassigned' => 'User Author Reassigned', + 'media_uploaded' => 'Media Uploaded', + 'media_updated' => 'Media Updated', + 'media_deleted' => 'Media Deleted', + 'comment_inserted' => 'Comment Inserted', + 'comment_status' => 'Comment Status Changed', + ]; + + /** + * Get the list of allowed webhook events. + * + * @return array Associative array of event keys and labels. + */ + public function get_allowed_events(): array { + $default_events = $this->default_events; + $mapped_events = SmartCacheEventMapper::getMappedEvents(); + $filtered_events = array_intersect_key( $default_events, $mapped_events ); + + return apply_filters( 'graphql_webhooks_allowed_events', $filtered_events ); + } + + /** + * Get the list of allowed HTTP methods. + * + * @return array Array of allowed HTTP methods. + */ + public function get_allowed_methods(): array { + $methods = [ 'POST', 'GET' ]; + return apply_filters( 'graphql_webhooks_allowed_methods', $methods ); + } + + /** + * Retrieve all published webhook entities. + * + * @return Webhook[] Array of Webhook entity objects. + */ + public function get_all(): array { + $webhooks = []; + + $posts = get_posts( [ + 'post_type' => 'graphql_webhook', + 'post_status' => 'publish', + 'numberposts' => -1, + ] ); + + foreach ( $posts as $post ) { + $webhooks[] = $this->mapPostToEntity( $post ); + } + + return $webhooks; + } + + /** + * Retrieve a webhook entity by its post ID. + * + * @param int $id The post ID of the webhook. + * @return Webhook|null The Webhook entity or null if not found or invalid post type. + */ + public function get( $id ): ?Webhook { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { + return null; + } + return $this->mapPostToEntity( $post ); + } + + /** + * Create a new webhook entity. + * + * @param Webhook $webhook The webhook entity to create. + * + * @return int|WP_Error Post ID on success, or WP_Error on failure. + */ + public function create( Webhook $webhook ) { + $validation = $this->validate( $webhook ); + if ( is_wp_error( $validation ) ) { + return $validation; + } + + $postId = wp_insert_post( [ + 'post_title' => $webhook->name, + 'post_type' => 'graphql_webhook', + 'post_status' => 'publish', + ], true ); + + if ( is_wp_error( $postId ) ) { + return $postId; + } + + update_post_meta( $postId, '_webhook_event', sanitize_text_field( $webhook->event ) ); + update_post_meta( $postId, '_webhook_url', esc_url_raw( $webhook->url ) ); + update_post_meta( $postId, '_webhook_method', strtoupper( $webhook->method ) ); + update_post_meta( $postId, '_webhook_headers', wp_json_encode( $webhook->headers ) ); + + return $postId; + } + + /** + * Update an existing webhook entity. + * + * @param int $id Post ID of the webhook to update. + * @param Webhook $webhook The webhook entity with updated data. + * + * @return bool|WP_Error True on success, or WP_Error on failure. + */ + public function update( int $id, Webhook $webhook ) { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { + return new WP_Error( 'invalid_webhook', __( 'Webhook not found.', 'wpgraphql-webhooks' ) ); + } + + // Validate using the Webhook entity + $validation = $this->validate( $webhook ); + if ( is_wp_error( $validation ) ) { + return $validation; + } + + $postData = [ + 'ID' => $id, + 'post_title' => sanitize_text_field( $webhook->name ), + ]; + + $updated = wp_update_post( $postData, true ); + if ( is_wp_error( $updated ) ) { + return $updated; + } + + update_post_meta( $id, '_webhook_event', sanitize_text_field( $webhook->event ) ); + update_post_meta( $id, '_webhook_url', esc_url_raw( $webhook->url ) ); + update_post_meta( $id, '_webhook_method', strtoupper( $webhook->method ) ); + update_post_meta( $id, '_webhook_headers', wp_json_encode( $webhook->headers ) ); + + return true; + } + + /** + * Delete a webhook entity by post ID. + * + * @param int $id Post ID of the webhook to delete. + * @return bool True if deleted, false otherwise. + */ + public function delete( $id ): bool { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { + return false; + } + + $deleted = wp_delete_post( $id, true ); + + return (bool) $deleted; + } + + /** + * Validate webhook entity before creation or update. + * + * @param Webhook $webhook The webhook entity to validate. + * @return bool|WP_Error True if valid, WP_Error if invalid. + */ + public function validate( Webhook $webhook ) { + $event = $webhook->event; + $url = $webhook->url; + $method = $webhook->method; + + if ( ! isset( $this->get_allowed_events()[ $event ] ) ) { + return new WP_Error( 'invalid_event', 'Invalid event type.' ); + } + if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { + return new WP_Error( 'invalid_url', 'Invalid URL.' ); + } + if ( ! in_array( strtoupper( $method ), $this->get_allowed_methods(), true ) ) { + return new WP_Error( 'invalid_method', 'Invalid HTTP method.' ); + } + return apply_filters( 'graphql_webhooks_validate_data', true, $event, $url, $method ); + } + + /** + * Map a WP_Post object to a Webhook entity. + * + * @param WP_Post $post The webhook post object. + * + * @return Webhook The mapped Webhook entity. + */ + private function mapPostToEntity( WP_Post $post ) { + $event = get_post_meta( $post->ID, '_webhook_event', true ); + $url = get_post_meta( $post->ID, '_webhook_url', true ); + $method = get_post_meta( $post->ID, '_webhook_method', true ) ?: 'POST'; + $headers = get_post_meta( $post->ID, '_webhook_headers', true ); + $headers = $headers ? json_decode( $headers, true ) : []; + + return new Webhook( + $post->ID, + $post->post_title, + $event, + $url, + $method, + $headers + ); + } +} diff --git a/plugins/wpgraphql-webhooks/src/Rest/WebhookTestEndpoint.php b/plugins/wpgraphql-webhooks/src/Rest/WebhookTestEndpoint.php new file mode 100644 index 00000000..025f159b --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Rest/WebhookTestEndpoint.php @@ -0,0 +1,166 @@ +repository = $repository; + } + + /** + * Register REST routes. + */ + public function register_routes(): void { + register_rest_route( + 'graphql-webhooks/v1', + '/test', + [ + 'methods' => 'POST', + 'callback' => [ $this, 'test_webhook' ], + 'permission_callback' => [ $this, 'permission_callback' ], + 'args' => [ + 'webhook_id' => [ + 'required' => true, + 'type' => 'integer', + 'sanitize_callback' => 'absint', + ], + ], + ] + ); + } + + /** + * Permission callback. + * + * @return bool Whether user has permission. + */ + public function permission_callback(): bool { + return current_user_can( 'manage_options' ); + } + + /** + * Test a webhook. + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response|WP_Error Response. + */ + public function test_webhook( WP_REST_Request $request ): WP_REST_Response|WP_Error { + $webhook_id = $request->get_param( 'webhook_id' ); + $webhook = $this->repository->get( $webhook_id ); + + if ( ! $webhook ) { + return new WP_Error( + 'webhook_not_found', + __( 'Webhook not found.', 'wpgraphql-webhooks' ), + [ 'status' => 404 ] + ); + } + + // Log test initiation + $test_timestamp = current_time( 'mysql' ); + error_log( "\n========== WEBHOOK TEST INITIATED ==========" ); + error_log( "Timestamp: {$test_timestamp}" ); + error_log( "Webhook ID: {$webhook_id}" ); + error_log( "Webhook Name: {$webhook->name}" ); + error_log( "Target URL: {$webhook->url}" ); + error_log( "HTTP Method: {$webhook->method}" ); + error_log( "Event: {$webhook->event}" ); + error_log( "Headers: " . wp_json_encode( $webhook->headers ) ); + error_log( "==========================================\n" ); + + // Create test payload + $test_payload = [ + 'event' => 'test', + 'timestamp' => $test_timestamp, + 'webhook' => [ + 'id' => $webhook->id, + 'name' => $webhook->name, + 'event' => $webhook->event, + ], + 'test_data' => [ + 'message' => 'This is a test webhook payload', + 'triggered_by' => wp_get_current_user()->user_login, + 'site_url' => get_site_url(), + ], + ]; + + // Allow filtering of test payload + $test_payload = apply_filters( 'graphql_webhooks_test_payload', $test_payload, $webhook ); + + // Trigger test event with enhanced logging + $start_time = microtime( true ); + + try { + do_action( 'wpgraphql_webhooks_test_event', $webhook, $test_payload ); + + $end_time = microtime( true ); + $duration = round( ( $end_time - $start_time ) * 1000, 2 ); // Convert to milliseconds + + error_log( "\n========== WEBHOOK TEST COMPLETED ==========" ); + error_log( "✅ SUCCESS: Test webhook dispatched" ); + error_log( "Duration: {$duration}ms" ); + error_log( "Completed at: " . current_time( 'mysql' ) ); + error_log( "==========================================\n" ); + + return new WP_REST_Response( + [ + 'success' => true, + 'message' => __( 'Test webhook dispatched successfully.', 'wpgraphql-webhooks' ), + 'details' => [ + 'webhook_id' => $webhook_id, + 'webhook_name' => $webhook->name, + 'target_url' => $webhook->url, + 'method' => $webhook->method, + 'duration_ms' => $duration, + 'timestamp' => $test_timestamp, + 'payload_size' => strlen( wp_json_encode( $test_payload ) ) . ' bytes', + ], + ], + 200 + ); + } catch ( \Exception $e ) { + error_log( "\n========== WEBHOOK TEST ERROR ==========" ); + error_log( "❌ ERROR: " . $e->getMessage() ); + error_log( "Stack trace: " . $e->getTraceAsString() ); + error_log( "========================================\n" ); + + return new WP_Error( + 'webhook_test_failed', + sprintf( + /* translators: %s: error message */ + __( 'Failed to dispatch test webhook: %s', 'wpgraphql-webhooks' ), + $e->getMessage() + ), + [ 'status' => 500 ] + ); + } + } +} diff --git a/plugins/wpgraphql-webhooks/src/Services/Interfaces/ServiceLocator.php b/plugins/wpgraphql-webhooks/src/Services/Interfaces/ServiceLocator.php new file mode 100644 index 00000000..a7cf2774 --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Services/Interfaces/ServiceLocator.php @@ -0,0 +1,28 @@ +factories[$name] = $factory; + unset($this->instances[$name]); + } + + public function has(string $name): bool { + return isset($this->factories[$name]); + } + + public function get(string $name) { + if (!isset($this->factories[$name])) { + throw new UnexpectedValueException("Service not found: {$name}"); + } + + if (!isset($this->instances[$name])) { + $this->instances[$name] = call_user_func($this->factories[$name]); + } + + return $this->instances[$name]; + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/Type/Webhook.php b/plugins/wpgraphql-webhooks/src/Type/Webhook.php new file mode 100644 index 00000000..a11f322e --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/Type/Webhook.php @@ -0,0 +1,78 @@ + __( 'A Webhook configuration object.', 'wpgraphql-webhooks' ), + 'fields' => [ + 'id' => [ + 'type' => 'ID', + 'description' => __( 'The global ID of the webhook.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return (string) $webhook->ID; + }, + ], + 'eventTrigger' => [ + 'type' => 'String', + 'description' => __( 'The event hook name that triggers this webhook.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return get_post_meta( $webhook->ID, '_event_trigger', true ); + }, + ], + 'title' => [ + 'type' => 'String', + 'description' => __( 'The title of the webhook.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return get_the_title( $webhook ); + }, + ], + 'enabled' => [ + 'type' => 'Boolean', + 'description' => __( 'Whether the webhook is enabled.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return (bool) get_post_meta( $webhook->ID, '_enabled', true ); + }, + ], + 'security' => [ + 'type' => 'String', + 'description' => __( 'Security information for the webhook.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return get_post_meta( $webhook->ID, '_security', true ); + }, + ], + 'handlerClass' => [ + 'type' => 'String', + 'description' => __( 'The handler class used for dispatching.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return get_post_meta( $webhook->ID, '_handler_class', true ); + }, + ], + 'handlerConfig' => [ + 'type' => 'String', + 'description' => __( 'Configuration for the handler.', 'wpgraphql-webhooks' ), + 'resolve' => function ($webhook) { + return get_post_meta( $webhook->ID, '_handler_config', true ); + }, + ], + ], + ] ); + } +} \ No newline at end of file diff --git a/plugins/wpgraphql-webhooks/src/TypeRegistry.php b/plugins/wpgraphql-webhooks/src/TypeRegistry.php new file mode 100644 index 00000000..b33ba51e --- /dev/null +++ b/plugins/wpgraphql-webhooks/src/TypeRegistry.php @@ -0,0 +1,90 @@ + + */ +function wpgraphql_webhooks_dependencies_not_ready(): array { + $deps = []; + + if ( ! class_exists( '\WPGraphQL' ) ) { + $deps[] = 'WPGraphQL'; + } + + return $deps; +} + +/** + * Initializes plugin. + */ +function wpgraphql_webhooks_init(): void { + wpgraphql_webhooks_constants(); + $not_ready = wpgraphql_webhooks_dependencies_not_ready(); + + if ( $not_ready === [] && defined( 'WPGRAPHQL_WEBHOOKS_PLUGIN_DIR' ) ) { + // Load text domain at the init hook + add_action( 'init', 'WPGraphQL\Webhooks\wpgraphql_webhooks_load_textdomain' ); + + require_once WPGRAPHQL_WEBHOOKS_PLUGIN_DIR . 'src/Plugin.php'; + $plugin = new \WPGraphQL\Webhooks\Plugin(); + $plugin::instance(); + return; + } + + foreach ( $not_ready as $dep ) { + add_action( + 'admin_notices', + static function () use ($dep) { + ?> +
+

+ +

+
+