From 50c654ecd9aabf59f1e64ad43f748520802bc9f6 Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Thu, 21 Nov 2024 16:38:43 -0600 Subject: [PATCH 1/4] remove subtree --- drop-ins/wp-memcached/.gitignore | 2 - drop-ins/wp-memcached/bin/test.sh | 148 - drop-ins/wp-memcached/composer.json | 38 - drop-ins/wp-memcached/composer.lock | 4416 ----------------- .../includes/adapter-interface.php | 160 - .../includes/memcache-adapter.php | 389 -- .../includes/memcached-adapter.php | 492 -- drop-ins/wp-memcached/includes/stats.php | 381 -- .../wp-memcached/includes/wp-object-cache.php | 1155 ----- drop-ins/wp-memcached/object-cache.php | 398 -- drop-ins/wp-memcached/phpcs.xml.dist | 29 - drop-ins/wp-memcached/phpunit.xml.dist | 25 - drop-ins/wp-memcached/psalm.xml.dist | 23 - drop-ins/wp-memcached/readme.md | 39 - .../wp-memcached/stubs/memcache-stubs.php | 444 -- .../wp-memcached/stubs/memcached-stubs.php | 1528 ------ .../wp-memcached/stubs/wordpress-stubs.php | 130 - drop-ins/wp-memcached/tests/bootstrap.php | 34 - .../tests/test-wp-object-cache.php | 889 ---- 19 files changed, 10720 deletions(-) delete mode 100644 drop-ins/wp-memcached/.gitignore delete mode 100755 drop-ins/wp-memcached/bin/test.sh delete mode 100644 drop-ins/wp-memcached/composer.json delete mode 100644 drop-ins/wp-memcached/composer.lock delete mode 100644 drop-ins/wp-memcached/includes/adapter-interface.php delete mode 100644 drop-ins/wp-memcached/includes/memcache-adapter.php delete mode 100644 drop-ins/wp-memcached/includes/memcached-adapter.php delete mode 100644 drop-ins/wp-memcached/includes/stats.php delete mode 100644 drop-ins/wp-memcached/includes/wp-object-cache.php delete mode 100644 drop-ins/wp-memcached/object-cache.php delete mode 100644 drop-ins/wp-memcached/phpcs.xml.dist delete mode 100644 drop-ins/wp-memcached/phpunit.xml.dist delete mode 100644 drop-ins/wp-memcached/psalm.xml.dist delete mode 100644 drop-ins/wp-memcached/readme.md delete mode 100644 drop-ins/wp-memcached/stubs/memcache-stubs.php delete mode 100644 drop-ins/wp-memcached/stubs/memcached-stubs.php delete mode 100644 drop-ins/wp-memcached/stubs/wordpress-stubs.php delete mode 100644 drop-ins/wp-memcached/tests/bootstrap.php delete mode 100644 drop-ins/wp-memcached/tests/test-wp-object-cache.php diff --git a/drop-ins/wp-memcached/.gitignore b/drop-ins/wp-memcached/.gitignore deleted file mode 100644 index cae6dff879..0000000000 --- a/drop-ins/wp-memcached/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/vendor/ -/coverage/ diff --git a/drop-ins/wp-memcached/bin/test.sh b/drop-ins/wp-memcached/bin/test.sh deleted file mode 100755 index f08bdee81e..0000000000 --- a/drop-ins/wp-memcached/bin/test.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/sh - -while [ $# -gt 0 ]; do - case "$1" in - --wp) - shift - WP_VERSION="$1" - ;; - - --multisite) - shift - WP_MULTISITE="$1" - ;; - - --php) - shift - PHP_VERSION="$1" - ;; - - --php-options) - shift - PHP_OPTIONS="$1" - ;; - - --phpunit) - shift - PHPUNIT_VERSION="$1" - ;; - - --network) - shift - NETWORK_NAME_OVERRIDE="$1" - ;; - - --dbhost) - shift - MYSQL_HOST_OVERRIDE="$1" - ;; - - --docker-options) - shift - DOCKER_OPTIONS="$1" - ;; - - *) - ARGS="${ARGS} $1" - ;; - esac - - shift -done - -: "${WP_VERSION:=latest}" -: "${WP_MULTISITE:=0}" -: "${PHP_VERSION:=""}" -: "${PHP_OPTIONS:=""}" -: "${PHPUNIT_VERSION:=""}" -: "${DOCKER_OPTIONS:=""}" - -PHP_OPTIONS="-d apc.enable_cli=1 ${PHP_OPTIONS}" - -export WP_VERSION -export WP_MULTISITE -export PHP_VERSION -export PHP_OPTIONS -export PHPUNIT_VERSION - -echo "--------------" -echo "Will test with WP_VERSION=${WP_VERSION} and WP_MULTISITE=${WP_MULTISITE}" -echo "--------------" -echo - -MARIADB_VERSION="10.3" - -UUID=$(date +%s000) -if [ -z "${NETWORK_NAME_OVERRIDE}" ]; then - NETWORK_NAME="tests-${UUID}" - docker network create "${NETWORK_NAME}" -else - NETWORK_NAME="${NETWORK_NAME_OVERRIDE}" -fi - -export MYSQL_USER=wordpress -export MYSQL_PASSWORD=wordpress -export MYSQL_DATABASE=wordpress_test - -db="" -if [ -z "${MYSQL_HOST_OVERRIDE}" ]; then - MYSQL_HOST="db-${UUID}" - db=$(docker run --rm --network "${NETWORK_NAME}" --name "${MYSQL_HOST}" -e MYSQL_ROOT_PASSWORD="wordpress" -e MARIADB_INITDB_SKIP_TZINFO=1 -e MYSQL_USER -e MYSQL_PASSWORD -e MYSQL_DATABASE -d "mariadb:${MARIADB_VERSION}") -else - MYSQL_HOST="${MYSQL_HOST_OVERRIDE}" -fi - -export MYSQL_HOST - -export MEMCACHED_HOST_1="mc1-${UUID}" -export MEMCACHED_HOST_2="mc2-${UUID}" -mc1=$(docker run --rm --network "${NETWORK_NAME}" --name "${MEMCACHED_HOST_1}" -d memcached:latest) -mc2=$(docker run --rm --network "${NETWORK_NAME}" --name "${MEMCACHED_HOST_2}" -d memcached:latest) - -cleanup() { - if [ -n "${db}" ]; then - docker rm -f "${db}" - fi - - if [ -n "${mc1}" ]; then - docker rm -f "${mc1}" - fi - - if [ -n "${mc2}" ]; then - docker rm -f "${mc2}" - fi - - if [ -z "${NETWORK_NAME_OVERRIDE}" ]; then - docker network rm "${NETWORK_NAME}" - fi -} - -trap cleanup EXIT - -if [ -z "${CI}" ]; then - interactive="-it" -else - interactive="" -fi - -# shellcheck disable=SC2086,SC2248,SC2312 # ARGS and DOCKER_OPTIONS must not be quoted -docker run \ - ${interactive} \ - --rm \ - --network "${NETWORK_NAME}" \ - -e WP_VERSION \ - -e WP_MULTISITE \ - -e PHP_VERSION \ - -e PHP_OPTIONS \ - -e PHPUNIT_VERSION \ - -e MYSQL_USER \ - -e MYSQL_PASSWORD \ - -e MYSQL_DB="${MYSQL_DATABASE}" \ - -e MYSQL_HOST \ - -e XDEBUG_MODE=coverage \ - -e MEMCACHED_HOST_1="${MEMCACHED_HOST_1}:11211" \ - -e MEMCACHED_HOST_2="${MEMCACHED_HOST_2}:11211" \ - ${DOCKER_OPTIONS} \ - -v "$(pwd):/home/circleci/project" \ - ghcr.io/automattic/vip-container-images/wp-test-runner:latest \ - ${ARGS} diff --git a/drop-ins/wp-memcached/composer.json b/drop-ins/wp-memcached/composer.json deleted file mode 100644 index 0038d9f0e2..0000000000 --- a/drop-ins/wp-memcached/composer.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "automattic/wp-cache-memcached", - "description": "Memcached Object Cache for WordPress", - "type": "wordpress-dropin", - "require": { - "php": ">= 7.4" - }, - "require-dev": { - "automattic/vipwpcs": "^2.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "phpcompatibility/phpcompatibility-wp": "^2.1", - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.0", - "wp-phpunit/wp-phpunit": "^6.1", - "yoast/phpunit-polyfills": "^1.0" - }, - "license": "MIT", - "authors": [ - { - "name": "Volodymyr Kolesnykov", - "email": "volodymyr@wildwolf.name" - } - ], - "config": { - "sort-packages": true, - "platform": { - "php": "7.4" - }, - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true - } - }, - "scripts": { - "phpcs": "phpcs", - "phpcs:fix": "phpcbf", - "psalm": "psalm" - } -} diff --git a/drop-ins/wp-memcached/composer.lock b/drop-ins/wp-memcached/composer.lock deleted file mode 100644 index 431a5cf175..0000000000 --- a/drop-ins/wp-memcached/composer.lock +++ /dev/null @@ -1,4416 +0,0 @@ -{ - "_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": "89c422a14c317e64af29d024db06cd13", - "packages": [], - "packages-dev": [ - { - "name": "amphp/amp", - "version": "v2.6.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "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", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" - }, - "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.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-20T17:52:18+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", - "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", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "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": "http://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-03-30T17:13:30+00:00" - }, - { - "name": "automattic/vipwpcs", - "version": "2.3.3", - "source": { - "type": "git", - "url": "https://github.com/Automattic/VIP-Coding-Standards.git", - "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", - "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", - "php": ">=5.4", - "sirbrillig/phpcs-variable-analysis": "^2.11.1", - "squizlabs/php_codesniffer": "^3.5.5", - "wp-coding-standards/wpcs": "^2.3" - }, - "require-dev": { - "php-parallel-lint/php-console-highlighter": "^0.5", - "php-parallel-lint/php-parallel-lint": "^1.0", - "phpcompatibility/php-compatibility": "^9", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" - }, - "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", - "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": "2021-09-29T16:20:23+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.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": "2022-01-17T14:14:24+00:00" - }, - { - "name": "composer/pcre", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" - }, - "type": "library", - "extra": { - "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.1.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": "2022-11-17T09:50:14+00:00" - }, - { - "name": "composer/semver", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "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": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/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": "2022-04-01T19:23:25+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", - "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", - "symfony/phpunit-bridge": "^6.0" - }, - "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": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.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": "2022-02-25T21:32:43+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", - "source": { - "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "Dealerdirect\\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/Dealerdirect/phpcodesniffer-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/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, - "time": "2022-02-04T12:51:07+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/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": "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.2", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", - "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.2" - }, - "time": "2022-03-02T22:36:06+00:00" - }, - { - "name": "fidry/cpu-core-counter", - "version": "0.4.1", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "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": "^9.5.26 || ^8.5.31", - "theofidry/php-cs-fixer-config": "^1.0", - "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/0.4.1" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2022-12-16T22:01:02+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", - "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", - "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.11.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2022-03-03T13:19:32+00:00" - }, - { - "name": "netresearch/jsonmapper", - "version": "v4.1.0", - "source": { - "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.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.1.0" - }, - "time": "2022-12-08T20:46:14+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.3", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^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.15.3" - }, - "time": "2023-01-16T22:05:37+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "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.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpcompatibility/php-compatibility", - "version": "9.3.5", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" - } - ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, - "time": "2019-12-27T09:44:58+00:00" - }, - { - "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.2", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", - "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", - "shasum": "" - }, - "require": { - "phpcompatibility/php-compatibility": "^9.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", - "paragonie/random_compat": "dev-master", - "paragonie/sodium_compat": "dev-master" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || 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", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" - }, - "time": "2022-10-25T01:46:02+00:00" - }, - { - "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", - "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", - "shasum": "" - }, - "require": { - "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || 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", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" - }, - "time": "2022-10-24T09:00:36+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.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "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": "account@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.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "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.6.2" - }, - "time": "2022-10-14T12:47:21+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.24", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library 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", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-01-26T08:26:55+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.5.28", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1 || ^2", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-01-14T12:32:24+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "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.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+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.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "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.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "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.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:10:38+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "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.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-03T09:37:03+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "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.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "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.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-02-14T08:28:10+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "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.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+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.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", - "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": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:17:30+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "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" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "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.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-12T14:47: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": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.10", - "source": { - "type": "git", - "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "0f25a3766f26df91d6bdda0c8931303fc85499d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/0f25a3766f26df91d6bdda0c8931303fc85499d7", - "reference": "0f25a3766f26df91d6bdda0c8931303fc85499d7", - "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", - "sirbrillig/phpcs-import-detection": "^1.1", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" - }, - "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": "2023-01-05T18:45:16+00:00" - }, - { - "name": "spatie/array-to-xml", - "version": "2.17.1", - "source": { - "type": "git", - "url": "https://github.com/spatie/array-to-xml.git", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": "^7.4|^8.0" - }, - "require-dev": { - "mockery/mockery": "^1.2", - "pestphp/pest": "^1.21", - "phpunit/phpunit": "^9.0", - "spatie/pest-plugin-snapshots": "^1.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\ArrayToXml\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://freek.dev", - "role": "Developer" - } - ], - "description": "Convert an array to xml", - "homepage": "https://github.com/spatie/array-to-xml", - "keywords": [ - "array", - "convert", - "xml" - ], - "support": { - "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2022-12-26T08:22:07+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "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": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2022-06-18T07:21:10+00:00" - }, - { - "name": "symfony/console", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", - "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.19" - }, - "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": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "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.2" - }, - "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": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "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.19" - }, - "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": "2023-01-14T19:14:44+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "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": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "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.2" - }, - "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": "2022-05-30T19:17:29+00:00" - }, - { - "name": "symfony/string", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb", - "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.19" - }, - "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": "2023-01-01T08:32:19+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "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.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "vimeo/psalm", - "version": "5.6.0", - "source": { - "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/e784128902dfe01d489c4123d69918a9f3c1eac5", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.10.0", - "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.0", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", - "sebastian/diff": "^4.0 || ^5.0", - "spatie/array-to-xml": "^2.17.0", - "symfony/console": "^4.1.6 || ^5.0 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0" - }, - "provide": { - "psalm/psalm": "self.version" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.0", - "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.5", - "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" - }, - "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": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev", - "dev-4.x": "4.x-dev", - "dev-3.x": "3.x-dev", - "dev-2.x": "2.x-dev", - "dev-1.x": "1.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" - ], - "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.6.0" - }, - "time": "2023-01-23T20:32:47+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "wp-coding-standards/wpcs", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "7da1894633f168fe244afc6de00d141f27517b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", - "reference": "7da1894633f168fe244afc6de00d141f27517b62", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.3.1" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", - "phpcompatibility/php-compatibility": "^9.0", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." - }, - "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", - "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" - }, - "time": "2020-05-13T23:57:56+00:00" - }, - { - "name": "wp-phpunit/wp-phpunit", - "version": "6.1.1", - "source": { - "type": "git", - "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "49521597fa525f762a50a4a6d22ed180839519fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/49521597fa525f762a50a4a6d22ed180839519fd", - "reference": "49521597fa525f762a50a4a6d22ed180839519fd", - "shasum": "" - }, - "type": "library", - "autoload": { - "files": [ - "__loaded.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0-or-later" - ], - "authors": [ - { - "name": "Evan Mattson", - "email": "me@aaemnnost.tv" - }, - { - "name": "WordPress Community", - "homepage": "https://wordpress.org/about/" - } - ], - "description": "WordPress core PHPUnit library", - "homepage": "https://github.com/wp-phpunit", - "keywords": [ - "phpunit", - "test", - "wordpress" - ], - "support": { - "docs": "https://github.com/wp-phpunit/docs", - "issues": "https://github.com/wp-phpunit/issues", - "source": "https://github.com/wp-phpunit/wp-phpunit" - }, - "time": "2022-11-02T12:52:44+00:00" - }, - { - "name": "yoast/phpunit-polyfills", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c", - "reference": "3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "require-dev": { - "yoast/yoastcs": "^2.2.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "autoload": { - "files": [ - "phpunitpolyfills-autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Team Yoast", - "email": "support@yoast.com", - "homepage": "https://yoast.com" - }, - { - "name": "Contributors", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" - } - ], - "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", - "keywords": [ - "phpunit", - "polyfill", - "testing" - ], - "support": { - "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", - "source": "https://github.com/Yoast/PHPUnit-Polyfills" - }, - "time": "2022-11-16T09:07:52+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">= 7.4" - }, - "platform-dev": [], - "platform-overrides": { - "php": "7.4" - }, - "plugin-api-version": "2.3.0" -} diff --git a/drop-ins/wp-memcached/includes/adapter-interface.php b/drop-ins/wp-memcached/includes/adapter-interface.php deleted file mode 100644 index eec8983108..0000000000 --- a/drop-ins/wp-memcached/includes/adapter-interface.php +++ /dev/null @@ -1,160 +0,0 @@ ->|array $memcached_servers - * - * @return void - */ - public function __construct( array $memcached_servers ); - - /** - * Get a list of all connection pools, indexed by group name. - * - * @psalm-return array - */ - public function get_connections(); - - /** - * Get a connection for each individual default server. - * - * @psalm-return array - */ - public function get_default_connections(); - - /** - * Get list of servers with connection errors. - * - * @psalm-return array - */ - public function get_connection_errors(); - - /** - * Close the memcached connections. - * - * @return bool - */ - public function close_connections(); - - /** - * Add an item under a new key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function add( $key, $connection_group, $data, $expiration ); - - /** - * Replace the item under an existing key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function replace( $key, $connection_group, $data, $expiration ); - - /** - * Store an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function set( $key, $connection_group, $data, $expiration ); - - /** - * Retrieve an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array{value: mixed, found: bool} - */ - public function get( $key, $connection_group ); - - /** - * Retrieve multiple items. - * - * @param array $keys List of keys to retrieve. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array|false - */ - public function get_multiple( $keys, $connection_group ); - - /** - * Delete an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return boolean - */ - public function delete( $key, $connection_group ); - - /** - * Delete multiple items. - * - * @param string[] $keys Array of the full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array - */ - public function delete_multiple( $keys, $connection_group ); - - /** - * Increment numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to increment the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function increment( $key, $connection_group, $offset ); - - /** - * Decrement numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to decrement the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function decrement( $key, $connection_group, $offset ); - - /** - * Set a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". - * - * @return void - */ - public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ); - - /** - * Get a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * - * @psalm-return array Key is the server's "host:port", value is returned from Memcached. - */ - public function get_with_redundancy( $key ); -} diff --git a/drop-ins/wp-memcached/includes/memcache-adapter.php b/drop-ins/wp-memcached/includes/memcache-adapter.php deleted file mode 100644 index 8ce08a1b53..0000000000 --- a/drop-ins/wp-memcached/includes/memcache-adapter.php +++ /dev/null @@ -1,389 +0,0 @@ - */ - private array $connections = []; - - /** @psalm-var array */ - private array $default_connections = []; - - /** @psalm-var array */ - private array $connection_errors = []; - - /** - * @psalm-param array>|array $memcached_servers - * - * @return void - */ - public function __construct( array $memcached_servers ) { - if ( is_int( key( $memcached_servers ) ) ) { - $memcached_servers = [ 'default' => $memcached_servers ]; - } - - /** @psalm-var array> $memcached_servers */ - foreach ( $memcached_servers as $bucket => $addresses ) { - $this->connections[ $bucket ] = new \Memcache(); - - foreach ( $addresses as $address ) { - $parsed_address = $this->parse_address( $address ); - $config = $this->get_config_options(); - - $this->connections[ $bucket ]->addServer( - $parsed_address['host'], - $parsed_address['port'], - $config['persistent'], - $config['weight'], - $config['timeout'], - $config['retry_interval'], - $config['status'], - $config['failure_callback'], - ); - - $this->connections[ $bucket ]->setCompressThreshold( $config['compress_threshold'], $config['min_compress_savings'] ); - - // Prepare individual connections to servers in the default bucket for flush_number redundancy. - if ( 'default' === $bucket ) { - $memcache = new \Memcache(); - - $memcache->addServer( - $parsed_address['host'], - $parsed_address['port'], - $config['persistent'], - $config['weight'], - $config['timeout'], - $config['retry_interval'], - $config['status'], - $config['failure_callback'], - ); - - $this->default_connections[ $parsed_address['host'] . ':' . $parsed_address['port'] ] = $memcache; - } - } - } - } - - /* - |-------------------------------------------------------------------------- - | Connection-related adapter methods. - |-------------------------------------------------------------------------- - */ - - /** - * Get a list of all connection pools, indexed by group name. - * - * @psalm-return array - */ - public function get_connections() { - return $this->connections; - } - - /** - * Get a connection for each individual default server. - * - * @psalm-return array - */ - public function get_default_connections() { - return array_values( $this->default_connections ); - } - - /** - * Get list of servers with connection errors. - * - * @psalm-return array - */ - public function &get_connection_errors() { - return $this->connection_errors; - } - - /** - * Close the memcached connections. - * Note that Memcache::close() doesn't close persistent connections, but does free up some memory. - * - * @return bool - */ - public function close_connections() { - // TODO: Should probably "close" $default_connections too? - foreach ( $this->connections as $connection ) { - $connection->close(); - } - - return true; - } - - /* - |-------------------------------------------------------------------------- - | Main adapter methods for memcached server interactions. - |-------------------------------------------------------------------------- - */ - - /** - * Add an item under a new key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function add( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ - return $mc->add( $key, $data, false, $expiration ); - } - - /** - * Replace the item under an existing key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function replace( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ - return $mc->replace( $key, $data, false, $expiration ); - } - - /** - * Store an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function set( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ - return $mc->set( $key, $data, false, $expiration ); - } - - /** - * Retrieve an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array{value: mixed, found: bool} - */ - public function get( $key, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - - $flags = false; - /** @psalm-suppress InvalidArgument $flags can be false */ - $value = $mc->get( $key, $flags ); - - /** @psalm-suppress RedundantCondition -- $flags is passed by reference so it may change*/ - return [ - 'value' => $value, - 'found' => false !== $flags, - ]; - } - - /** - * Retrieve multiple items. - * - * @param array $keys List of keys to retrieve. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array|false - */ - public function get_multiple( $keys, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - - $return = []; - foreach ( $keys as $key ) { - $flags = false; - /** @psalm-suppress InvalidArgument $flags can be false */ - $value = $mc->get( $key, $flags ); - - /** @psalm-suppress RedundantCondition -- $flags is passed by reference so it may change*/ - if ( false !== $flags ) { - // Only return if we found the value, similar to Memcached::getMulti() - $return[ $key ] = $value; - } - } - - return $return; - } - - /** - * Delete an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return boolean - */ - public function delete( $key, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - return $mc->delete( $key ); - } - - /** - * Delete multiple items. - * - * @param string[] $keys Array of the full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array - */ - public function delete_multiple( $keys, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - - $return = []; - foreach ( $keys as $key ) { - $return[ $key ] = $mc->delete( $key ); - } - - return $return; - } - - /** - * Increment numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to increment the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function increment( $key, $connection_group, $offset ) { - $mc = $this->get_connection( $connection_group ); - return $mc->increment( $key, $offset ); - } - - /** - * Decrement numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to decrement the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function decrement( $key, $connection_group, $offset ) { - $mc = $this->get_connection( $connection_group ); - return $mc->decrement( $key, $offset ); - } - - /** - * Set a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". - * - * @return void - */ - public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ) { - foreach ( $this->default_connections as $server_string => $mc ) { - if ( is_null( $servers_to_update ) || in_array( $server_string, $servers_to_update, true ) ) { - $mc->set( $key, $data, $expiration ); - } - } - } - - /** - * Get a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * - * @psalm-return array Key is the server's "host:port", value is returned from Memcached. - */ - public function get_with_redundancy( $key ) { - $values = []; - - foreach ( $this->default_connections as $server_string => $mc ) { - /** @psalm-suppress MixedAssignment */ - $values[ $server_string ] = $mc->get( $key ); - } - - return $values; - } - - /* - |-------------------------------------------------------------------------- - | Utils. - |-------------------------------------------------------------------------- - */ - - /** - * @param int|string $group - * @return \Memcache - */ - private function get_connection( $group ) { - return $this->connections[ (string) $group ] ?? $this->connections['default']; - } - - /** - * @param string $address - * @psalm-return array{host: string, port: int} - */ - private function parse_address( string $address ): array { - $default_port = ini_get( 'memcache.default_port' ) ? ini_get( 'memcache.default_port' ) : '11211'; - - if ( 'unix://' == substr( $address, 0, 7 ) ) { - $host = $address; - $port = 0; - } else { - $items = explode( ':', $address, 2 ); - $host = $items[0]; - $port = isset( $items[1] ) ? intval( $items[1] ) : intval( $default_port ); - } - - return [ - 'host' => $host, - 'port' => $port, - ]; - } - - /** - * @param string $host - * @param string $port - */ - public function failure_callback( $host, $port ): void { - $this->connection_errors[] = [ - 'host' => $host, - 'port' => $port, - ]; - } - - /** - * @psalm-return array{ - * persistent: bool, - * weight: int, - * timeout: int, - * retry_interval: int, - * status: bool, - * compress_threshold: int, - * min_compress_savings:float, - * failure_callback: callable, - * } - */ - private function get_config_options(): array { - return [ - 'persistent' => true, - 'weight' => 1, - 'timeout' => 1, - 'retry_interval' => 15, - 'status' => true, - 'compress_threshold' => 20000, - 'min_compress_savings' => 0.2, - 'failure_callback' => [ $this, 'failure_callback' ], - ]; - } -} diff --git a/drop-ins/wp-memcached/includes/memcached-adapter.php b/drop-ins/wp-memcached/includes/memcached-adapter.php deleted file mode 100644 index 292adbb102..0000000000 --- a/drop-ins/wp-memcached/includes/memcached-adapter.php +++ /dev/null @@ -1,492 +0,0 @@ - */ - private array $connections = []; - - /** @psalm-var array */ - private array $default_connections = []; - - /** @psalm-var array */ - private array $redundancy_server_keys = []; - - /** @psalm-var array */ - private array $connection_errors = []; - - /** - * @psalm-param array>|array $memcached_servers - * - * @return void - */ - public function __construct( array $memcached_servers ) { - if ( is_int( key( $memcached_servers ) ) ) { - $memcached_servers = [ 'default' => $memcached_servers ]; - } - - /** @psalm-var array> $memcached_servers */ - foreach ( $memcached_servers as $bucket => $addresses ) { - $bucket_servers = []; - - foreach ( $addresses as $index => $address ) { - $parsed_address = $this->parse_address( $address ); - $server = [ - 'host' => $parsed_address['host'], - 'port' => $parsed_address['port'], - 'weight' => 1, - ]; - - $bucket_servers[] = $server; - - // Prepare individual connections to servers in the default bucket for flush_number redundancy. - if ( 'default' === $bucket ) { - // Deprecated in this adapter. As long as no requests are made from these pools, the connections should never be established. - $this->default_connections[] = $this->create_connection_pool( 'redundancy-' . $index, [ $server ] ); - } - } - - $this->connections[ $bucket ] = $this->create_connection_pool( 'bucket-' . $bucket, $bucket_servers ); - } - - $this->redundancy_server_keys = $this->get_server_keys_for_redundancy(); - } - - /* - |-------------------------------------------------------------------------- - | Connection-related adapter methods. - |-------------------------------------------------------------------------- - */ - - /** - * Get a list of all connection pools, indexed by group name. - * - * @psalm-return array - */ - public function get_connections() { - return $this->connections; - } - - /** - * Get a connection for each individual default server. - * - * @psalm-return array - */ - public function get_default_connections() { - return $this->default_connections; - } - - /** - * Get list of servers with connection errors. - * - * @psalm-return array - */ - public function &get_connection_errors() { - // Not supported atm. We could look at Memcached::getResultCode() after each call, - // looking for MEMCACHED_CONNECTION_FAILURE for example. - // But it wouldn't tell us the exact host/port that failed. - // Memcached::getStats() is another possibility, though not the same behavior-wise. - return $this->connection_errors; - } - - /** - * Close the memcached connections. - * @return bool - */ - public function close_connections() { - // Memcached::quit() closes persistent connections, which we don't want to do. - return true; - } - - /* - |-------------------------------------------------------------------------- - | Main adapter methods for memcached server interactions. - |-------------------------------------------------------------------------- - */ - - /** - * Add an item under a new key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function add( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - return $mc->add( $this->normalize_key( $key ), $data, $expiration ); - } - - /** - * Replace the item under an existing key. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function replace( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - return $mc->replace( $this->normalize_key( $key ), $data, $expiration ); - } - - /** - * Store an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * - * @return boolean True on success, false on failure. - */ - public function set( $key, $connection_group, $data, $expiration ) { - $mc = $this->get_connection( $connection_group ); - return $mc->set( $this->normalize_key( $key ), $data, $expiration ); - } - - /** - * Retrieve an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array{value: mixed, found: bool} - */ - public function get( $key, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - /** @psalm-suppress MixedAssignment */ - $value = $mc->get( $this->normalize_key( $key ) ); - - return [ - 'value' => $value, - 'found' => \Memcached::RES_NOTFOUND !== $mc->getResultCode(), - ]; - } - - /** - * Retrieve multiple items. - * - * @param array $keys List of keys to retrieve. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array|false Will not return anything in the array for unfound keys. - * @psalm-suppress MixedAssignment - */ - public function get_multiple( $keys, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - $mapped_keys = $this->normalize_keys_with_mapping( $keys ); - - $results = []; - if ( count( $mapped_keys ) > 1000 ) { - // Sending super large multiGets to a memcached server results in - // extremely inflated read/response buffers which can consume a lot memory perpetually. - // So instead we'll chunk up and send multiple reasonably-sized requests. - $chunked_keys = array_chunk( $mapped_keys, 1000, true ); - - foreach( $chunked_keys as $chunk_of_keys ) { - /** @psalm-var array|false $partial_results */ - $partial_results = $mc->getMulti( array_keys( $chunk_of_keys ) ); - - if ( ! is_array( $partial_results ) ) { - // If any of the lookups fail, we'll bail on the whole thing to be consistent. - return false; - } - - $results = array_merge( $results, $partial_results ); - } - } else { - /** @psalm-var array|false $results */ - $results = $mc->getMulti( array_keys( $mapped_keys ) ); - - if ( ! is_array( $results ) ) { - return false; - } - } - - $return = []; - foreach ( $results as $result_key => $result_value ) { - $original_key = isset( $mapped_keys[ $result_key ] ) ? $mapped_keys[ $result_key ] : $result_key; - $return[ $original_key ] = $result_value; - } - - return $return; - } - - /** - * Delete an item. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return boolean - */ - public function delete( $key, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - return $mc->delete( $this->normalize_key( $key ) ); - } - - /** - * Delete multiple items. - * - * @param string[] $keys Array of the full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * - * @return array - */ - public function delete_multiple( $keys, $connection_group ) { - $mc = $this->get_connection( $connection_group ); - $mapped_keys = $this->normalize_keys_with_mapping( $keys ); - - /** @psalm-var array $results */ - $results = $mc->deleteMulti( array_keys( $mapped_keys ) ); - - $return = []; - foreach ( $results as $result_key => $result_value ) { - $original_key = isset( $mapped_keys[ $result_key ] ) ? $mapped_keys[ $result_key ] : $result_key; - - // deleteMulti() returns true on success, but one of the Memcached::RES_* constants if failed. - $return[ $original_key ] = true === $result_value; - } - - return $return; - } - - /** - * Increment numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to increment the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function increment( $key, $connection_group, $offset ) { - $mc = $this->get_connection( $connection_group ); - return $mc->increment( $this->normalize_key( $key ), $offset ); - } - - /** - * Decrement numeric item's value. - * - * @param string $key The full key, including group & flush prefixes. - * @param string $connection_group The group, used to find the right connection pool. - * @param int $offset The amount by which to decrement the item's value. - * - * @return false|int The new item's value on success or false on failure. - */ - public function decrement( $key, $connection_group, $offset ) { - $mc = $this->get_connection( $connection_group ); - return $mc->decrement( $this->normalize_key( $key ), $offset ); - } - - /** - * Set a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * @param mixed $data The contents to store in the cache. - * @param int $expiration When to expire the cache contents, in seconds. - * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". - * - * @return void - */ - public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ) { - $mc = $this->connections['default']; - - foreach ( $this->redundancy_server_keys as $server_string => $server_key ) { - if ( is_null( $servers_to_update ) || in_array( $server_string, $servers_to_update, true ) ) { - $mc->setByKey( $server_key, $key, $data, $expiration ); - } - } - } - - /** - * Get a key across all default memcached servers. - * - * @param string $key The full key, including group & flush prefixes. - * - * @psalm-return array Key is the server's "host:port", value is returned from Memcached. - */ - public function get_with_redundancy( $key ) { - $mc = $this->connections['default']; - - $values = []; - foreach ( $this->redundancy_server_keys as $server_string => $server_key ) { - /** @psalm-suppress MixedAssignment */ - $values[ $server_string ] = $mc->getByKey( $server_key, $key ); - } - - return $values; - } - - /* - |-------------------------------------------------------------------------- - | Utils. - |-------------------------------------------------------------------------- - */ - - /** - * @param int|string $group - * @return \Memcached - */ - private function get_connection( $group ) { - return $this->connections[ (string) $group ] ?? $this->connections['default']; - } - - /** - * Servers and configurations are persisted between requests. - * So we only want to add servers when the configuration has changed. - * - * @param string $name - * @psalm-param array $servers - * @return \Memcached - */ - private function create_connection_pool( $name, $servers ) { - $mc = new \Memcached( $name ); - - // Servers and configurations are persisted between requests. - /** @psalm-var array $existing_servers */ - $existing_servers = $mc->getServerList(); - - // Check if the servers have changed since they were registered. - $needs_refresh = count( $existing_servers ) !== count( $servers ); - foreach ( $servers as $index => $server ) { - $existing_host = $existing_servers[ $index ]['host'] ?? null; - $existing_port = $existing_servers[ $index ]['port'] ?? null; - - if ( $existing_host !== $server['host'] || $existing_port !== $server['port'] ) { - $needs_refresh = true; - } - } - - if ( $needs_refresh ) { - $mc->resetServerList(); - - $servers_to_add = []; - foreach ( $servers as $server ) { - $servers_to_add[] = [ $server['host'], $server['port'], $server['weight'] ]; - } - - $mc->addServers( $servers_to_add ); - $mc->setOptions( $this->get_config_options() ); - } - - return $mc; - } - - /** - * @param string $address - * @psalm-return array{host: string, port: int} - */ - private function parse_address( string $address ): array { - $default_port = 11211; - - if ( 'unix://' == substr( $address, 0, 7 ) ) { - // Note: This slighly differs from the memcache adapater, as memcached wants unix:// stripped off. - $host = substr( $address, 7 ); - $port = 0; - } else { - $items = explode( ':', $address, 2 ); - $host = $items[0]; - $port = isset( $items[1] ) ? intval( $items[1] ) : $default_port; - } - - return [ - 'host' => $host, - 'port' => $port, - ]; - } - - private function get_config_options(): array { - /** @psalm-suppress TypeDoesNotContainType */ - $serializer = \Memcached::HAVE_IGBINARY && extension_loaded( 'igbinary' ) ? \Memcached::SERIALIZER_IGBINARY : \Memcached::SERIALIZER_PHP; - - // TODO: Check memcached.compression_threshold / memcached.compression_factor - // These are all TBD still. - return [ - \Memcached::OPT_BINARY_PROTOCOL => false, - \Memcached::OPT_SERIALIZER => $serializer, - \Memcached::OPT_CONNECT_TIMEOUT => 1000, - \Memcached::OPT_COMPRESSION => true, - \Memcached::OPT_TCP_NODELAY => true, - ]; - } - - /** - * We want to find a unique string that, when hashed, will map to each - * of the default servers in our pool. This will allow us to - * talk with each server individually and set a key with redundancy. - * - * @psalm-return array Key is the server's "host:port", value is the server_key that accesses it. - */ - private function get_server_keys_for_redundancy(): array { - $default_pool = $this->connections['default']; - $servers = $default_pool->getServerList(); - - $server_keys = []; - for ( $i = 0; $i < 1000; $i++ ) { - $test_key = 'redundancy_key_' . $i; - - /** @psalm-var array{host: string, port: int, weight: int}|false $result */ - $result = $default_pool->getServerByKey( $test_key ); - - if ( ! $result ) { - continue; - } - - $server_string = $result['host'] . ':' . $result['port']; - if ( ! isset( $server_keys[ $server_string ] ) ) { - $server_keys[ $server_string ] = $test_key; - } - - // Keep going until every server is accounted for (capped at 1000 attempts - which is incredibly unlikely unless there are tons of servers). - if ( count( $server_keys ) === count( $servers ) ) { - break; - } - } - - return $server_keys; - } - - /** - * Memcached can only handle keys with a length of 250 or less. - * The Memcache extension automatically truncates. Memcached does not. - * Instead of truncation, which can lead to some hidden consequences, - * we can hash the string and use a shortened version when interacting - * with the Memcached servers. - * - * @param string $key - * @psalm-return string - */ - private function normalize_key( $key ) { - if ( strlen( $key ) <= 250 ) { - return $key; - } else { - return substr( $key, 0, 200 ) . ':truncated:' . md5( $key ); - } - } - - /** - * Reduce key lenths while providing a map of new_key => original_key. - * - * @param string[] $keys - * @psalm-return array - */ - private function normalize_keys_with_mapping( $keys ) { - $mapped_keys = []; - - foreach ( $keys as $key ) { - $mapped_keys[ $this->normalize_key( $key ) ] = $key; - } - - return $mapped_keys; - } -} diff --git a/drop-ins/wp-memcached/includes/stats.php b/drop-ins/wp-memcached/includes/stats.php deleted file mode 100644 index ec27734a7c..0000000000 --- a/drop-ins/wp-memcached/includes/stats.php +++ /dev/null @@ -1,381 +0,0 @@ - */ - public array $stats = []; - - /** - * @psalm-var array> - */ - public array $group_ops = []; - - public float $time_total = 0; - public int $size_total = 0; - - public float $slow_op_microseconds = 0.005; // 5 ms - - private string $key_salt; - - public function __construct( string $key_salt ) { - $this->key_salt = $key_salt; - - $this->stats = [ - 'get' => 0, - 'get_local' => 0, - 'get_multi' => 0, - 'set' => 0, - 'set_local' => 0, - 'add' => 0, - 'delete' => 0, - 'delete_local' => 0, - 'slow-ops' => 0, - ]; - } - - /* - |-------------------------------------------------------------------------- - | Stat tracking. - |-------------------------------------------------------------------------- - */ - - /** - * Keep stats for a memcached operation. - * - * @param string $op The operation taking place, such as "set" or "get". - * @param string|string[] $keys The memcached key/keys involved in the operation. - * @param string $group The group the keys are in. - * @param ?int $size The size of the data invovled in the operation. - * @param ?float $time The time the operation took. - * @param string $comment Extra notes about the operation. - * - * @return void - */ - public function group_ops_stats( $op, $keys, $group, $size = null, $time = null, $comment = '' ) { - $this->increment_stat( $op ); - - // Don't keep further details about the local operations. - if ( false !== strpos( $op, '_local' ) ) { - return; - } - - if ( ! is_null( $size ) ) { - $this->size_total += $size; - } - - if ( ! is_null( $time ) ) { - $this->time_total += $time; - } - - $keys = $this->strip_memcached_keys( $keys ); - - if ( $time > $this->slow_op_microseconds && 'get_multi' !== $op ) { - $this->increment_stat( 'slow-ops' ); - - /** @psalm-var string|null $backtrace */ - $backtrace = function_exists( 'wp_debug_backtrace_summary' ) ? wp_debug_backtrace_summary() : null; // phpcs:ignore - $this->group_ops['slow-ops'][] = array( $op, $keys, $size, $time, $comment, $group, $backtrace ); - } - - $this->group_ops[ $group ][] = array( $op, $keys, $size, $time, $comment ); - } - - /** - * Increment the stat counter for a memcached operation. - * - * @param string $field The stat field/group being incremented. - * @param int $num Amount to increment by. - * - * @return void - */ - public function increment_stat( $field, $num = 1 ) { - if ( ! isset( $this->stats[ $field ] ) ) { - $this->stats[ $field ] = $num; - } else { - $this->stats[ $field ] += $num; - } - } - - /* - |-------------------------------------------------------------------------- - | Utils. - |-------------------------------------------------------------------------- - */ - - /** - * Key format: key_salt:flush_number:table_prefix:key_name - * - * We want to strip the `key_salt:flush_number` part to not leak the memcached keys. - * If `key_salt` is set we strip `'key_salt:flush_number`, otherwise just strip the `flush_number` part. - * - * @param string|string[] $keys - * @return string|string[] - */ - public function strip_memcached_keys( $keys ) { - $keys = is_array( $keys ) ? $keys : [ $keys ]; - - foreach ( $keys as $index => $value ) { - $offset = 0; - - // Strip off the key salt piece. - if ( ! empty( $this->key_salt ) ) { - $salt_piece = strpos( $value, ':' ); - $offset = false === $salt_piece ? 0 : $salt_piece + 1; - } - - // Strip off the flush number. - $flush_piece = strpos( $value, ':', $offset ); - $start = false === $flush_piece ? $offset : $flush_piece; - $keys[ $index ] = substr( $value, $start + 1 ); - } - - if ( 1 === count( $keys ) ) { - return $keys[0]; - } - - return $keys; - } - - /* - |-------------------------------------------------------------------------- - | Stats markup output. - |-------------------------------------------------------------------------- - */ - - /** - * Returns the collected raw stats. - */ - public function get_stats(): array { - $stats = [ - 'operation_counts' => $this->stats, - 'operations' => [], - 'groups' => [], - 'slow-ops' => [], - 'slow-ops-groups' => [], - 'totals' => [ - 'query_time' => $this->time_total, - 'size' => $this->size_total, - ], - ]; - - foreach ( $this->group_ops as $cache_group => $dataset ) { - $cache_group = empty( $cache_group ) ? 'default' : $cache_group; - - foreach ( $dataset as $data ) { - $operation = $data[0]; - $op = [ - 'key' => $data[1], - 'size' => $data[2], - 'time' => $data[3], - 'group' => $cache_group, - 'result' => $data[4], - ]; - - if ( 'slow-ops' === $cache_group ) { - $key = 'slow-ops'; - $groups_key = 'slow-ops-groups'; - $op['group'] = $data[5]; - $op['backtrace'] = $data[6]; - } else { - $key = 'operations'; - $groups_key = 'groups'; - } - - $stats[ $key ][ $operation ][] = $op; - if ( ! in_array( $op['group'], $stats[ $groups_key ], true ) ) { - $stats[ $groups_key ][] = $op['group']; - } - } - } - - return $stats; - } - - public function stats(): void { - $this->js_toggle(); - - $total_query_time = number_format_i18n( (float) sprintf( '%0.1f', $this->time_total * 1000 ), 1 ) . ' ms'; - - $total_size = size_format( $this->size_total, 2 ); - $total_size = false === $total_size ? '0 B' : $total_size; - - echo '

Total memcached query time:' . esc_html( $total_query_time ) . '

'; - echo "\n"; - echo '

Total memcached size:' . esc_html( $total_size ) . '

'; - echo "\n"; - - foreach ( $this->stats as $stat => $n ) { - if ( empty( $n ) ) { - continue; - } - - echo '

'; - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo $this->colorize_debug_line( "$stat $n" ); - echo '

'; - } - - echo "\n"; - - echo "
\n"; - foreach ( $groups as $group ) { - $group_name = empty( $group ) ? 'default' : $group; - - $current = $active_group == $group ? 'style="display: block"' : 'style="display: none"'; - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo "
\n"; - echo '

' . esc_html( $group_titles[ $group ] ) . '

' . "\n"; - echo "
\n";
-			foreach ( $this->group_ops[ $group ] as $index => $arr ) {
-				echo esc_html( sprintf( '%3d ', $index ) );
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-				echo $this->get_group_ops_line( $index, $arr );
-			}
-			echo "
\n"; - echo '
'; - } - - echo '
'; - } - - public function js_toggle(): void { - echo " - - "; - } - - /** - * @param string $line - * @param string $trailing_html - * @return string - */ - public function colorize_debug_line( $line, $trailing_html = '' ) { - $colors = array( - 'get' => 'green', - 'get_local' => 'lightgreen', - 'get_multi' => 'fuchsia', - 'get_multiple' => 'navy', - 'set' => 'purple', - 'set_local' => 'orchid', - 'add' => 'blue', - 'delete' => 'red', - 'delete_local' => 'tomato', - 'slow-ops' => 'crimson', - ); - - $cmd = substr( $line, 0, (int) strpos( $line, ' ' ) ); - - // Start off with a neutral default color, and use a more specific one if possible. - $color_for_cmd = isset( $colors[ $cmd ] ) ? $colors[ $cmd ] : 'brown'; - - $cmd2 = "" . esc_html( $cmd ) . ''; - - return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "$trailing_html\n"; - } - - /** - * @param string|int $index - * @param array $arr - * @psalm-param array{0: string, 1: string|string[], 2: int|null, 3: float|null, 4: string, 5: string, 6: string|null } $arr - * - * @return string - */ - public function get_group_ops_line( $index, $arr ) { - // operation - $line = "{$arr[0]} "; - - // keys - // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode - $json_encoded_key = json_encode( $arr[1] ); - $line .= $json_encoded_key . ' '; - - // comment - if ( ! empty( $arr[4] ) ) { - $line .= "{$arr[4]} "; - } - - // size - if ( isset( $arr[2] ) ) { - $size = size_format( $arr[2], 2 ); - - if ( $size ) { - $line .= '(' . $size . ') '; - } - } - - // time - if ( isset( $arr[3] ) ) { - $line .= '(' . number_format_i18n( (float) sprintf( '%0.1f', $arr[3] * 1000 ), 1 ) . ' ms)'; - } - - // backtrace - $bt_link = ''; - if ( isset( $arr[6] ) ) { - $key_hash = md5( $index . $json_encoded_key ); - $bt_link = " Toggle Backtrace"; - $bt_link .= "'; - } - - return $this->colorize_debug_line( $line, $bt_link ); - } -} diff --git a/drop-ins/wp-memcached/includes/wp-object-cache.php b/drop-ins/wp-memcached/includes/wp-object-cache.php deleted file mode 100644 index 0a22ebfaf3..0000000000 --- a/drop-ins/wp-memcached/includes/wp-object-cache.php +++ /dev/null @@ -1,1155 +0,0 @@ - - */ - public array $flush_number = []; - - public ?int $global_flush_number = null; - public string $global_prefix = ''; - public string $blog_prefix = ''; - public string $key_salt = ''; - - /** - * Global cache groups (network-wide rather than site-specific). - * @var string[] - */ - public array $global_groups = []; - - /** - * Non-persistent cache groups (will not write to Memcached servers). - * @var string[] - */ - public array $no_mc_groups = []; - - public int $default_expiration = 0; - public int $max_expiration = 2592000; // 30 days - - private Adapter_Interface $adapter; - private Stats $stats_helper; - - /** @psalm-var array */ - public array $mc = []; - - /** @psalm-var array */ - public array $default_mcs = []; - - /** - * @psalm-var array - */ - public array $cache = []; - - // Stats tracking. - public array $stats = []; - public array $group_ops = []; - public int $cache_hits = 0; - public int $cache_misses = 0; - public float $time_start = 0; - public float $time_total = 0; - public int $size_total = 0; - public float $slow_op_microseconds = 0.005; // 5 ms - - // TODO: Deprecate. These appear to be unused. - public string $old_flush_key = 'flush_number'; - public bool $cache_enabled = true; - public array $stats_callback = []; - /** @psalm-var array */ - public array $connection_errors = []; - - /** - * @global array>|array|null $memcached_servers - * @global string $table_prefix - * @global int|numeric-string $blog_id - * - * @param ?Adapter_Interface $adapter Optionally inject the adapter layer, useful for unit testing. - * @psalm-suppress UnsupportedReferenceUsage - */ - public function __construct( $adapter = null ) { - global $blog_id, $table_prefix, $memcached_servers; - - $this->global_groups = [ $this->global_flush_group ]; - - $is_ms = function_exists( 'is_multisite' ) && is_multisite(); - - $this->global_prefix = $is_ms || ( defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; - $this->blog_prefix = (string) ( $is_ms ? $blog_id : $table_prefix ); - - $use_memcached = defined( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION' ) && AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION; - if ( ! is_null( $adapter ) ) { - $this->adapter = $adapter; - } else { - $servers = is_array( $memcached_servers ) ? $memcached_servers : [ 'default' => [ '127.0.0.1:11211' ] ]; - $this->adapter = $use_memcached ? new Memcached_Adapter( $servers ) : new Memcache_Adapter( $servers ); - } - - $this->salt_keys( WP_CACHE_KEY_SALT, $use_memcached ); - - // Backwards compatability as these have been public properties. Ideally we deprecate and remove in the future. - $this->mc = $this->adapter->get_connections(); - $this->default_mcs = $this->adapter->get_default_connections(); - $this->connection_errors =& $this->adapter->get_connection_errors(); - - $this->stats_helper = new Stats( $this->key_salt ); - - // Also for backwards compatability since these have been public properties. - $this->stats =& $this->stats_helper->stats; - $this->group_ops =& $this->stats_helper->group_ops; - $this->time_total =& $this->stats_helper->time_total; - $this->size_total =& $this->stats_helper->size_total; - $this->slow_op_microseconds =& $this->stats_helper->slow_op_microseconds; - $this->cache_hits =& $this->stats['get']; - $this->cache_misses =& $this->stats['add']; - } - - /* - |-------------------------------------------------------------------------- - | The main methods used by the cache API. - |-------------------------------------------------------------------------- - */ - - /** - * Adds data to the cache if it doesn't already exist. - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True on success, false on failure or if cache key and group already exist. - */ - public function add( $key, $data, $group = 'default', $expire = 0 ) { - $is_alloptions = 'alloptions' === $key && 'options' === $group; - $key = $this->key( $key, $group ); - - if ( is_object( $data ) ) { - $data = clone $data; - } - - if ( $this->is_non_persistent_group( $group ) ) { - if ( isset( $this->cache[ $key ] ) ) { - return false; - } - - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => false, - ]; - - return true; - } - - if ( isset( $this->cache[ $key ]['value'] ) && false !== $this->cache[ $key ]['value'] ) { - return false; - } - - $expire = $this->get_expiration( $expire ); - $size = $this->get_data_size( $data ); - - $this->timer_start(); - $result = $this->adapter->add( $key, $group, $data, $expire ); - $elapsed = $this->timer_stop(); - - $comment = ''; - if ( isset( $this->cache[ $key ] ) ) { - $comment .= ' [lc already]'; - } - - if ( false === $result ) { - $comment .= ' [mc already]'; - } - - $this->group_ops_stats( 'add', $key, $group, $size, $elapsed, $comment ); - - // Special handling for alloptions on WP < 6.2 (before pre_wp_load_alloptions filter). - // A) If the add() fails, - if ( false === $result && $is_alloptions && version_compare( $GLOBALS['wp_version'], '6.2', '<' ) ) { - // B) And there is still nothing retrieved with a remote get(), - if ( false === $this->get( 'alloptions', 'options', true ) ) { - // C) Then we'll keep the fresh value in the runtime cache to help keep performance stable. - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => false, - ]; - } - - return $result; - } - - if ( $result ) { - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => true, - ]; - } elseif ( isset( $this->cache[ $key ]['value'] ) && false === $this->cache[ $key ]['value'] ) { - /* - * Here we unset local cache if remote add failed and local cache value is equal to `false` in order - * to update the local cache anytime we get a new information from remote server. This way, the next - * cache get will go to remote server and will fetch recent data. - */ - unset( $this->cache[ $key ] ); - } - - return $result; - } - - /** - * Adds multiple values to the cache in one call. - * - * @param mixed[] $data Array of keys and values to be added. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false on failure or if cache key and group already exist. - */ - public function add_multiple( $data, $group = '', $expire = 0 ) { - $result = []; - - /** @psalm-suppress MixedAssignment - $value is unknown/mixed */ - foreach ( $data as $key => $value ) { - $result[ $key ] = $this->add( $key, $value, $group, $expire ); - } - - return $result; - } - - /** - * Replaces the contents in the cache, if contents already exist. - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True if contents were replaced, false on failure or if the original value did not exist. - */ - public function replace( $key, $data, $group = 'default', $expire = 0 ) { - $key = $this->key( $key, $group ); - - if ( is_object( $data ) ) { - $data = clone $data; - } - - if ( $this->is_non_persistent_group( $group ) ) { - if ( ! isset( $this->cache[ $key ] ) ) { - return false; - } - - $this->cache[ $key ]['value'] = $data; - return true; - } - - $expire = $this->get_expiration( $expire ); - $size = $this->get_data_size( $data ); - - $this->timer_start(); - $result = $this->adapter->replace( $key, $group, $data, $expire ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'replace', $key, $group, $size, $elapsed ); - - if ( $result ) { - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => true, - ]; - } else { - // Remove from local cache if the replace failed, as it may no longer exist. - unset( $this->cache[ $key ] ); - } - - return $result; - } - - /** - * Sets the data contents into the cache. - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. How long until the cahce contents will expire (in seconds). - * - * @return bool True if contents were set, false if failed. - */ - public function set( $key, $data, $group = 'default', $expire = 0 ) { - $key = $this->key( $key, $group ); - - if ( is_object( $data ) ) { - $data = clone $data; - } - - if ( $this->is_non_persistent_group( $group ) ) { - $this->group_ops_stats( 'set_local', $key, $group ); - - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => false, - ]; - - return true; - } - - $expire = $this->get_expiration( $expire ); - $size = $this->get_data_size( $data ); - - $this->timer_start(); - $result = $this->adapter->set( $key, $group, $data, $expire ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'set', $key, $group, $size, $elapsed ); - - $this->cache[ $key ] = [ - 'value' => $data, - 'found' => $result, - ]; - - return $result; - } - - /** - * Sets multiple values to the cache in one call. - * - * @param mixed[] $data Array of key and value to be set. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Value is true on success, false on failure. - */ - public function set_multiple( $data, $group = '', $expire = 0 ) { - $result = []; - - /** @psalm-suppress MixedAssignment - $value is mixed */ - foreach ( $data as $key => $value ) { - // TODO: Could try to make Memcached::setMulti() work, though the return structure differs. - $result[ $key ] = $this->set( $key, $value, $group, $expire ); - } - - return $result; - } - - /** - * Retrieves the cache contents, if it exists. - * - * @param int|string $key The key under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * @param bool $force Optional. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @param bool $found Optional. Whether the key was found in the cache (passed by reference). - * Disambiguates a return of false, a storable value. Default null. - * @return mixed|false The cache contents on success, false on failure to retrieve contents. - */ - public function get( $key, $group = 'default', $force = false, &$found = null ) { - $key = $this->key( $key, $group ); - - if ( $force && $this->is_non_persistent_group( $group ) ) { - // There's nothing to "force" retrieve. - $force = false; - } - - if ( isset( $this->cache[ $key ] ) && ! $force ) { - /** @psalm-suppress MixedAssignment */ - $value = is_object( $this->cache[ $key ]['value'] ) ? clone $this->cache[ $key ]['value'] : $this->cache[ $key ]['value']; - $found = $this->cache[ $key ]['found']; - - $this->group_ops_stats( 'get_local', $key, $group, null, null, 'local' ); - - return $value; - } - - if ( $this->is_non_persistent_group( $group ) ) { - // This is a bit suboptimal, but keeping for back-compat for now. - $found = false; - - $this->cache[ $key ] = [ - 'value' => false, - 'found' => false, - ]; - - $this->group_ops_stats( 'get_local', $key, $group, null, null, 'not_in_local' ); - - return false; - } - - $this->timer_start(); - /** @psalm-suppress MixedAssignment */ - $result = $this->adapter->get( $key, $group ); - $elapsed = $this->timer_stop(); - - $this->cache[ $key ] = $result; - $found = $result['found']; - - if ( $result['found'] ) { - $this->group_ops_stats( 'get', $key, $group, $this->get_data_size( $result['value'] ), $elapsed, 'memcache' ); - } else { - $this->group_ops_stats( 'get', $key, $group, null, $elapsed, 'not_in_memcache' ); - } - - return $result['value']; - } - - /** - * Retrieves multiple values from the cache in one call. - * - * @param array $keys Array of keys under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * @param bool $force Optional. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @return mixed[] Array of return values, grouped by key. Each value is either - * the cache contents on success, or false on failure. - */ - public function get_multiple( $keys, $group = 'default', $force = false ) { - $uncached_keys = []; - $return = []; - $return_cache = []; - - if ( $force && $this->is_non_persistent_group( $group ) ) { - // There's nothing to "force" retrieve. - $force = false; - } - - // First, fetch what we can from runtime cache. - foreach ( $keys as $key ) { - $cache_key = $this->key( $key, $group ); - - if ( isset( $this->cache[ $cache_key ] ) && ! $force ) { - /** @psalm-suppress MixedAssignment */ - $return[ $key ] = is_object( $this->cache[ $cache_key ]['value'] ) ? clone $this->cache[ $cache_key ]['value'] : $this->cache[ $cache_key ]['value']; - - $this->group_ops_stats( 'get_local', $cache_key, $group, null, null, 'local' ); - } elseif ( $this->is_non_persistent_group( $group ) ) { - $return[ $key ] = false; - $return_cache[ $cache_key ] = [ - 'value' => false, - 'found' => false, - ]; - - $this->group_ops_stats( 'get_local', $cache_key, $group, null, null, 'not_in_local' ); - } else { - $uncached_keys[ $key ] = $cache_key; - } - } - - if ( ! empty( $uncached_keys ) ) { - $this->timer_start(); - $values = $this->adapter->get_multiple( array_values( $uncached_keys ), $group ); - $elapsed = $this->timer_stop(); - - $values = false === $values ? [] : $values; - foreach ( $uncached_keys as $key => $cache_key ) { - $found = array_key_exists( $cache_key, $values ); - /** @psalm-suppress MixedAssignment */ - $value = $found ? $values[ $cache_key ] : false; - - /** @psalm-suppress MixedAssignment */ - $return[ $key ] = $value; - $return_cache[ $cache_key ] = [ - 'value' => $value, - 'found' => $found, - ]; - } - - $this->group_ops_stats( 'get_multiple', array_values( $uncached_keys ), $group, $this->get_data_size( array_values( $values ) ), $elapsed ); - } - - $this->cache = array_merge( $this->cache, $return_cache ); - return $return; - } - - /** - * Retrieves multiple values from the cache in one call. - * - * @param array> $groups Array of keys, indexed by group. - * Example: $groups['group-name'] = [ 'key1', 'key2' ] - * - * @return mixed[] Array of return values, grouped by key. Each value is either - * the cache contents on success, or false on failure. - */ - public function get_multi( $groups ) { - $return = []; - - foreach ( $groups as $group => $keys ) { - $results = $this->get_multiple( $keys, $group ); - - foreach ( $keys as $key ) { - // This feels like a bug, as the full cache key is not useful to consumers. But alas, should be deprecating this method soon anyway. - $cache_key = $this->key( $key, $group ); - - /** @psalm-suppress MixedAssignment */ - $return[ $cache_key ] = isset( $results[ $key ] ) ? $results[ $key ] : false; - } - } - - return $return; - } - - /** - * Removes the contents of the cache key in the group. - * - * @param int|string $key What the contents in the cache are called. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * - * @return bool True on success, false on failure or if the contents were not deleted. - */ - public function delete( $key, $group = 'default' ) { - $key = $this->key( $key, $group ); - - if ( $this->is_non_persistent_group( $group ) ) { - $result = isset( $this->cache[ $key ] ); - unset( $this->cache[ $key ] ); - - return $result; - } - - $this->timer_start(); - $deleted = $this->adapter->delete( $key, $group ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'delete', $key, $group, null, $elapsed ); - - // Remove from local cache regardless of the result. - unset( $this->cache[ $key ] ); - - return $deleted; - } - - /** - * Deletes multiple values from the cache in one call. - * - * @param array $keys Array of keys to be deleted. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false if the contents were not deleted. - */ - public function delete_multiple( $keys, $group = '' ) { - if ( $this->is_non_persistent_group( $group ) ) { - $return = []; - - foreach ( $keys as $key ) { - $cache_key = $this->key( $key, $group ); - - $deleted = isset( $this->cache[ $cache_key ] ); - unset( $this->cache[ $cache_key ] ); - - $return[ $key ] = $deleted; - } - - return $return; - } - - $mapped_keys = $this->map_keys( $keys, $group ); - - $this->timer_start(); - $results = $this->adapter->delete_multiple( array_keys( $mapped_keys ), $group ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'delete_multiple', array_keys( $mapped_keys ), $group, null, $elapsed ); - - $return = []; - foreach ( $results as $cache_key => $deleted ) { - $return[ $mapped_keys[ $cache_key ] ] = $deleted; - - // Remove from local cache regardless of the result. - unset( $this->cache[ $cache_key ] ); - } - - return $return; - } - - /** - * Increments numeric cache item's value. - * - * @param int|string $key The cache key to increment. - * @param int $offset Optional. The amount by which to increment the item's value. - * Default 1. - * @param string $group Optional. The group the key is in. Default 'default'. - * @return int|false The item's new value on success, false on failure. - */ - public function incr( $key, $offset = 1, $group = 'default' ) { - $key = $this->key( $key, $group ); - - if ( $this->is_non_persistent_group( $group ) ) { - if ( ! isset( $this->cache[ $key ] ) || ! is_int( $this->cache[ $key ]['value'] ) ) { - return false; - } - - $this->cache[ $key ]['value'] += $offset; - return $this->cache[ $key ]['value']; - } - - $this->timer_start(); - $incremented = $this->adapter->increment( $key, $group, $offset ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'increment', $key, $group, null, $elapsed ); - - $this->cache[ $key ] = [ - 'value' => $incremented, - 'found' => false !== $incremented, - ]; - - return $incremented; - } - - /** - * Decrements numeric cache item's value. - * - * @param int|string $key The cache key to decrement. - * @param int $offset Optional. The amount by which to decrement the item's value. - * Default 1. - * @param string $group Optional. The group the key is in. Default 'default'. - * @return int|false The item's new value on success, false on failure. - */ - public function decr( $key, $offset = 1, $group = 'default' ) { - $key = $this->key( $key, $group ); - - if ( $this->is_non_persistent_group( $group ) ) { - if ( ! isset( $this->cache[ $key ] ) || ! is_int( $this->cache[ $key ]['value'] ) ) { - return false; - } - - $new_value = $this->cache[ $key ]['value'] - $offset; - if ( $new_value < 0 ) { - $new_value = 0; - } - - $this->cache[ $key ]['value'] = $new_value; - return $this->cache[ $key ]['value']; - } - - $this->timer_start(); - $decremented = $this->adapter->decrement( $key, $group, $offset ); - $elapsed = $this->timer_stop(); - - $this->group_ops_stats( 'decrement', $key, $group, null, $elapsed ); - - $this->cache[ $key ] = [ - 'value' => $decremented, - 'found' => false !== $decremented, - ]; - - return $decremented; - } - - /** - * Clears the object cache of all data. - * - * Purposely does not use the memcached flush method, - * as that acts on the entire memcached server, affecting all sites. - * Instead, we rotate the key prefix for the current site, - * along with the global key when flushing the main site. - * - * @return true Always returns true. - */ - public function flush() { - $this->cache = []; - - $flush_number = $this->new_flush_number(); - - $this->rotate_site_keys( $flush_number ); - if ( is_main_site() ) { - $this->rotate_global_keys( $flush_number ); - } - - return true; - } - - /** - * Unsupported: Removes all cache items in a group. - * - * @param string $_group Name of group to remove from cache. - * @return bool Returns false, as there is no support for group flushes. - */ - public function flush_group( $_group ) { - return false; - } - - /** - * Removes all cache items from the in-memory runtime cache. - * Also reset the local stat-related tracking for individual operations. - * - * @return true Always returns true. - */ - public function flush_runtime() { - $this->cache = []; - $this->group_ops = []; - - return true; - } - - /** - * Sets the list of global cache groups. - * - * @param string|string[] $groups List of groups that are global. - * @return void - */ - public function add_global_groups( $groups ) { - if ( ! is_array( $groups ) ) { - $groups = (array) $groups; - } - - $this->global_groups = array_unique( array_merge( $this->global_groups, $groups ) ); - } - - /** - * Sets the list of non-persistent groups. - * - * @param string|string[] $groups List of groups that will not be saved to persistent cache. - * @return void - */ - public function add_non_persistent_groups( $groups ) { - if ( ! is_array( $groups ) ) { - $groups = (array) $groups; - } - - $this->no_mc_groups = array_unique( array_merge( $this->no_mc_groups, $groups ) ); - } - - /** - * Switches the internal blog ID. - * - * This changes the blog ID used to create keys in blog specific groups. - * - * @param int $blog_id Blog ID. - * @return void - */ - public function switch_to_blog( $blog_id ) { - global $table_prefix; - - /** @psalm-suppress RedundantCastGivenDocblockType **/ - $blog_id = (int) $blog_id; - - $this->blog_prefix = (string) ( is_multisite() ? $blog_id : $table_prefix ); - } - - /** - * Close the connections. - * - * @return bool - */ - public function close() { - return $this->adapter->close_connections(); - } - - - /* - |-------------------------------------------------------------------------- - | Internal methods that deal with flush numbers, the pseudo-cache-flushing mechanic. - | Public methods here may be deprecated & made private in the future. - |-------------------------------------------------------------------------- - */ - - /** - * Get the flush number prefix, used for creating the key string. - * - * @param string|int $group - * @return string - */ - public function flush_prefix( $group ) { - if ( $group === $this->flush_group || $group === $this->global_flush_group ) { - // Never flush the flush numbers. - $number = '_'; - } elseif ( false !== array_search( $group, $this->global_groups ) ) { - $number = $this->get_global_flush_number(); - } else { - $number = $this->get_blog_flush_number(); - } - - return $number . ':'; - } - - /** - * Get the global group flush number. - * - * @return int - */ - public function get_global_flush_number() { - if ( ! isset( $this->global_flush_number ) ) { - $this->global_flush_number = $this->get_flush_number( $this->global_flush_group ); - } - - return $this->global_flush_number; - } - - /** - * Get the blog's flush number. - * - * @return int - */ - public function get_blog_flush_number() { - if ( ! isset( $this->flush_number[ $this->blog_prefix ] ) ) { - $this->flush_number[ $this->blog_prefix ] = $this->get_flush_number( $this->flush_group ); - } - - return $this->flush_number[ $this->blog_prefix ]; - } - - /** - * Get the flush number for a specific group. - * - * @param string $group - * @return int - */ - public function get_flush_number( $group ) { - $flush_number = $this->get_max_flush_number( $group ); - - if ( empty( $flush_number ) ) { - // If there was no flush number anywhere, make a new one. This flushes the cache. - $flush_number = $this->new_flush_number(); - $this->set_flush_number( $flush_number, $group ); - } - - return $flush_number; - } - - /** - * Set the flush number for a specific group. - * - * @param int $value - * @param string $group - * @return void - */ - public function set_flush_number( $value, $group ) { - $key = $this->key( $this->flush_key, $group ); - $expire = 0; - $size = 19; // size of the microsecond timestamp serialized - - $this->timer_start(); - $this->adapter->set_with_redundancy( $key, $value, $expire ); - $elapsed = $this->timer_stop(); - - $average_time_elapsed = $elapsed / count( $this->default_mcs ); - foreach ( $this->default_mcs as $_default_mc ) { - $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $average_time_elapsed, 'replication' ); - } - } - - /** - * Get the highest flush number from all default servers, replicating if needed. - * - * @param string $group - * @return int|false - */ - public function get_max_flush_number( $group ) { - $key = $this->key( $this->flush_key, $group ); - $size = 19; // size of the microsecond timestamp serialized - - $this->timer_start(); - $values = $this->adapter->get_with_redundancy( $key ); - $elapsed = $this->timer_stop(); - - $replication_servers_count = max( count( $this->default_mcs ), 1 ); - $average_time_elapsed = $elapsed / $replication_servers_count; - - /** @psalm-suppress MixedAssignment */ - foreach ( $values as $result ) { - if ( false === $result ) { - $this->group_ops_stats( 'get_flush_number', $key, $group, null, $average_time_elapsed, 'not_in_memcache' ); - } else { - $this->group_ops_stats( 'get_flush_number', $key, $group, $size, $average_time_elapsed, 'memcache' ); - } - } - - $values = array_map( 'intval', $values ); - /** @psalm-suppress ArgumentTypeCoercion */ - $max = max( $values ); - - if ( $max <= 0 ) { - return false; - } - - $servers_to_update = []; - foreach ( $values as $server_string => $value ) { - if ( $value < $max ) { - $servers_to_update[] = $server_string; - } - } - - // Replicate to servers not having the max. - if ( ! empty( $servers_to_update ) ) { - $expire = 0; - - $this->timer_start(); - $this->adapter->set_with_redundancy( $key, $max, $expire, $servers_to_update ); - $elapsed = $this->timer_stop(); - - $average_time_elapsed = $elapsed / count( $servers_to_update ); - foreach ( $servers_to_update as $updated_server ) { - $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $average_time_elapsed, 'replication_repair' ); - } - } - - return $max; - } - - /** - * Rotate the flush number for the site/blog. - * - * @param ?int $flush_number - * @return void - */ - public function rotate_site_keys( $flush_number = null ) { - if ( is_null( $flush_number ) ) { - $flush_number = $this->new_flush_number(); - } - - $this->set_flush_number( $flush_number, $this->flush_group ); - $this->flush_number[ $this->blog_prefix ] = $flush_number; - } - - /** - * Rotate the global flush number. - * - * @param ?int $flush_number - * @return void - */ - public function rotate_global_keys( $flush_number = null ) { - if ( is_null( $flush_number ) ) { - $flush_number = $this->new_flush_number(); - } - - $this->set_flush_number( $flush_number, $this->global_flush_group ); - $this->global_flush_number = $flush_number; - } - - /** - * Generate new flush number. - * - * @return int - */ - public function new_flush_number(): int { - return intval( microtime( true ) * 1e6 ); - } - - /* - |-------------------------------------------------------------------------- - | Utility methods. Internal use only. - | Public methods here may be deprecated & made private in the future. - |-------------------------------------------------------------------------- - */ - - /** - * Generate the key we'll use to interact with memcached. - * Note: APCU requires this to be public right now. - * - * @param int|string $key - * @param int|string $group - * @return string - */ - public function key( $key, $group ): string { - if ( empty( $group ) ) { - $group = 'default'; - } - - $result = sprintf( - '%s%s%s:%s:%s', - $this->key_salt, - $this->flush_prefix( $group ), - array_search( $group, $this->global_groups ) !== false ? $this->global_prefix : $this->blog_prefix, - $group, - $key - ); - - return preg_replace( '/\\s+/', '', $result ); - } - - /** - * Map the full cache key to the original key. - * - * @param array $keys - * @param string $group - * @return array - */ - protected function map_keys( $keys, $group ): array { - $results = []; - - foreach ( $keys as $key ) { - $results[ $this->key( $key, $group ) ] = $key; - } - - return $results; - } - - /** - * Get the memcached instance for the specified group. - * - * @param int|string $group - * @return Memcache|Memcached - */ - public function get_mc( $group ) { - if ( isset( $this->mc[ $group ] ) ) { - return $this->mc[ $group ]; - } - - return $this->mc['default']; - } - - /** - * Sanitize the expiration time. - * - * @psalm-param int|numeric-string|float $expire - */ - private function get_expiration( $expire ): int { - $expire = intval( $expire ); - if ( $expire <= 0 || $expire > $this->max_expiration ) { - $expire = $this->default_expiration; - } - - return $expire; - } - - /** - * Check if the group is set up for non-persistent cache. - * - * @param string|int $group - * @return bool - */ - private function is_non_persistent_group( $group ) { - return in_array( $group, $this->no_mc_groups, true ); - } - - /** - * Estimate the (uncompressed) data size. - * - * @param mixed $data - * @return int - * @psalm-return 0|positive-int - */ - public function get_data_size( $data ): int { - if ( is_string( $data ) ) { - return strlen( $data ); - } - - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize - return strlen( serialize( $data ) ); - } - - /** - * Sets the key salt property. - * - * @param mixed $key_salt - * @param bool $add_mc_prefix - * @return void - */ - public function salt_keys( $key_salt, $add_mc_prefix = false ) { - $key_salt = is_string( $key_salt ) && strlen( $key_salt ) ? $key_salt : ''; - $key_salt = $add_mc_prefix ? $key_salt . '_mc' : ''; - - $this->key_salt = empty( $key_salt ) ? '' : $key_salt . ':'; - } - - public function timer_start(): bool { - $this->time_start = microtime( true ); - return true; - } - - public function timer_stop(): float { - return microtime( true ) - $this->time_start; - } - - /** - * TODO: Deprecate. - * - * @param string $host - * @param string $port - */ - public function failure_callback( $host, $port ): void { - $this->connection_errors[] = array( - 'host' => $host, - 'port' => $port, - ); - } - - /* - |-------------------------------------------------------------------------- - | Stat-related tracking & output. - | A lot of the below should be deprecated/removed in the future. - |-------------------------------------------------------------------------- - */ - - /** - * Echoes the stats of the caching operations that have taken place. - * Ideally this should be the only method left public in this section. - * - * @return void Outputs the info directly, nothing is returned. - */ - public function stats() { - $this->stats_helper->stats(); - } - - /** - * Sets the key salt property. - * - * @param string $op The operation taking place, such as "set" or "get". - * @param string|string[] $keys The memcached key/keys involved in the operation. - * @param string $group The group the keys are in. - * @param ?int $size The size of the data invovled in the operation. - * @param ?float $time The time the operation took. - * @param string $comment Extra notes about the operation. - * - * @return void - */ - public function group_ops_stats( $op, $keys, $group, $size = null, $time = null, $comment = '' ) { - $this->stats_helper->group_ops_stats( $op, $keys, $group, $size, $time, $comment ); - } - - /** - * Returns the collected raw stats. - */ - public function get_stats(): array { - return $this->stats_helper->get_stats(); - } - - /** - * @param string $field The stat field/group being incremented. - * @param int $num Amount to increment by. - */ - public function increment_stat( $field, $num = 1 ): void { - $this->stats_helper->increment_stat( $field, $num ); - } - - /** - * @param string|string[] $keys - * @return string|string[] - */ - public function strip_memcached_keys( $keys ) { - return $this->stats_helper->strip_memcached_keys( $keys ); - } - - public function js_toggle(): void { - $this->stats_helper->js_toggle(); - } - - /** - * @param string $line - * @param string $trailing_html - * @return string - */ - public function colorize_debug_line( $line, $trailing_html = '' ) { - return $this->stats_helper->colorize_debug_line( $line, $trailing_html ); - } - - /** - * @param string|int $index - * @param array $arr - * @psalm-param array{0: string, 1: string|string[], 2: int|null, 3: float|null, 4: string, 5: string, 6: string|null } $arr - * - * @return string - */ - public function get_group_ops_line( $index, $arr ) { - return $this->stats_helper->get_group_ops_line( $index, $arr ); - } -} diff --git a/drop-ins/wp-memcached/object-cache.php b/drop-ins/wp-memcached/object-cache.php deleted file mode 100644 index 3aaebbe93a..0000000000 --- a/drop-ins/wp-memcached/object-cache.php +++ /dev/null @@ -1,398 +0,0 @@ -add( $key, $data, $group, (int) $expire ); -} - -/** - * Adds multiple values to the cache in one call. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param array $data Array of keys and values to be set. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false if cache key and group already exist. - * @psalm-param mixed[] $data - */ -function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) { - global $wp_object_cache; - - /** @psalm-suppress RedundantCastGivenDocblockType */ - return $wp_object_cache->add_multiple( $data, $group, (int) $expire ); -} - -/** - * Replaces the contents of the cache with new data. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key The key for the cache data that should be replaced. - * @param mixed $data The new data to store in the cache. - * @param string $group Optional. The group for the cache data that should be replaced. - * Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True if contents were replaced, false if original value does not exist. - * @psalm-suppress RedundantCast - */ -function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { - global $wp_object_cache; - - return $wp_object_cache->replace( $key, $data, $group, (int) $expire ); -} - -/** - * Saves the data to the cache. - * - * Differs from wp_cache_add() and wp_cache_replace() in that it will always write data. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key The cache key to use for retrieval later. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Enables the same key - * to be used across groups. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True on success, false on failure. - */ -function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { - global $wp_object_cache; - - if ( ! defined( 'WP_INSTALLING' ) ) { - /** @psalm-suppress RedundantCastGivenDocblockType */ - return $wp_object_cache->set( $key, $data, $group, (int) $expire ); - } - - return $wp_object_cache->delete( $key, $group ); -} - -/** - * Sets multiple values to the cache in one call. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param array $data Array of keys and values to be set. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false on failure. - * @psalm-param mixed[] $data - */ -function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) { - global $wp_object_cache; - - if ( ! defined( 'WP_INSTALLING' ) ) { - /** @psalm-suppress RedundantCastGivenDocblockType */ - return $wp_object_cache->set_multiple( $data, $group, (int) $expire ); - } - - return $wp_object_cache->delete_multiple( array_keys( $data ), $group ); -} - -/** - * Retrieves the cache contents from the cache by key and group. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key The key under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param bool $force Optional. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @param bool $found Optional. Whether the key was found in the cache (passed by reference). - * Disambiguates a return of false, a storable value. Default null. - * @return mixed|false The cache contents on success, false on failure to retrieve contents. - */ -function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { - global $wp_object_cache; - - $value = apply_filters( 'pre_wp_cache_get', false, $key, $group, $force ); - if ( false !== $value ) { - $found = true; - return $value; - } - - return $wp_object_cache->get( $key, $group, $force, $found ); -} - -/** - * Retrieves multiple values from the cache in one call. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param array $keys Array of keys under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param bool $force Optional. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @return array Array of return values, grouped by key. Each value is either - * the cache contents on success, or false on failure. - * @psalm-param (int|string)[] $keys - * @psalm-return mixed[] - */ -function wp_cache_get_multiple( $keys, $group = '', $force = false ) { - global $wp_object_cache; - - return $wp_object_cache->get_multiple( $keys, $group, $force ); -} - -/** - * Retrieves multiple values from the cache in one call. - * TODO: Deprecate - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param array> $groups Array of keys, indexed by group. Example: $groups['group-name'] = [ 'key1', 'key2' ] - * - * @return array Array of return values, with the full cache key as the index. Each value is either the cache contents on success, or false on failure. - */ -function wp_cache_get_multi( $groups ) { - global $wp_object_cache; - - return $wp_object_cache->get_multi( $groups ); -} - -/** - * Removes the cache contents matching key and group. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key What the contents in the cache are called. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @return bool True on successful removal, false on failure. - */ -function wp_cache_delete( $key, $group = '' ) { - global $wp_object_cache; - - return $wp_object_cache->delete( $key, $group ); -} - -/** - * Deletes multiple values from the cache in one call. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param array $keys Array of keys under which the cache to deleted. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false if the contents were not deleted. - * @psalm-param (int|string)[] $keys - */ -function wp_cache_delete_multiple( array $keys, $group = '' ) { - global $wp_object_cache; - - return $wp_object_cache->delete_multiple( $keys, $group ); -} - -/** - * Increments numeric cache item's value. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key The key for the cache contents that should be incremented. - * @param int $offset Optional. The amount by which to increment the item's value. Default 1. - * @param string $group Optional. The group the key is in. Default empty. - * @return int|false The item's new value on success, false on failure. - */ -function wp_cache_incr( $key, $offset = 1, $group = '' ) { - global $wp_object_cache; - - /** @psalm-suppress RedundantCastGivenDocblockType */ - return $wp_object_cache->incr( $key, (int) $offset, $group ); -} - -/** - * Decrements numeric cache item's value. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int|string $key The cache key to decrement. - * @param int $offset Optional. The amount by which to decrement the item's value. Default 1. - * @param string $group Optional. The group the key is in. Default empty. - * @return int|false The item's new value on success, false on failure. - */ -function wp_cache_decr( $key, $offset = 1, $group = '' ) { - global $wp_object_cache; - - /** @psalm-suppress RedundantCastGivenDocblockType */ - return $wp_object_cache->decr( $key, (int) $offset, $group ); -} - -/** - * Removes all cache items. - * - * @see WP_Object_Cache::flush() - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @return bool True on success, false on failure. - */ -function wp_cache_flush() { - global $wp_object_cache; - - return $wp_object_cache->flush(); -} - -/** - * Removes all cache items from the in-memory runtime cache. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @return bool True on success, false on failure. - */ -function wp_cache_flush_runtime() { - global $wp_object_cache; - - return $wp_object_cache->flush_runtime(); -} - -/** - * Unsupported: Removes all cache items in a group, if the object cache implementation supports it. - * - * Before calling this function, always check for group flushing support using the - * `wp_cache_supports( 'flush_group' )` function. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param string $_group Name of group to remove from cache. - * @return bool True if group was flushed, false otherwise. - */ -function wp_cache_flush_group( $_group ) { - global $wp_object_cache; - - return $wp_object_cache->flush_group( $_group ); -} - -/** - * Determines whether the object cache implementation supports a particular feature. - * - * @param string $feature Name of the feature to check for. Possible values include: - * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple', - * 'flush_runtime', 'flush_group'. - * @return bool True if the feature is supported, false otherwise. - */ -function wp_cache_supports( $feature ) { - switch ( $feature ) { - case 'add_multiple': - case 'set_multiple': - case 'get_multiple': - case 'delete_multiple': - case 'flush_runtime': - return true; - - default: - return false; - } -} - -/** - * Closes the cache. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @return bool - */ -function wp_cache_close() { - global $wp_object_cache; - - return $wp_object_cache->close(); -} - -/** - * Adds a group or set of groups to the list of global groups. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param string|string[] $groups A group or an array of groups to add. - * @return void - */ -function wp_cache_add_global_groups( $groups ) { - global $wp_object_cache; - - $wp_object_cache->add_global_groups( $groups ); -} - -/** - * Adds a group or set of groups to the list of non-persistent groups. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param string|string[] $groups A group or an array of groups to add. - * @return void - */ -function wp_cache_add_non_persistent_groups( $groups ) { - global $wp_object_cache; - - $wp_object_cache->add_non_persistent_groups( $groups ); -} - -/** - * Switches the internal blog ID. - * This changes the blog id used to create keys in blog specific groups. - * - * @global WP_Object_Cache $wp_object_cache Object cache global instance. - * - * @param int $blog_id Site ID. - * @return void - */ -function wp_cache_switch_to_blog( $blog_id ) { - global $wp_object_cache; - - /** @psalm-suppress RedundantCastGivenDocblockType */ - $wp_object_cache->switch_to_blog( (int) $blog_id ); -} diff --git a/drop-ins/wp-memcached/phpcs.xml.dist b/drop-ins/wp-memcached/phpcs.xml.dist deleted file mode 100644 index 89e8989919..0000000000 --- a/drop-ins/wp-memcached/phpcs.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - Custom ruleset for VIP Go mu-plugins - - . - - /\.git/* - /vendor/* - /stubs/* - - - - - - - - - - - - - - - - - - - - diff --git a/drop-ins/wp-memcached/phpunit.xml.dist b/drop-ins/wp-memcached/phpunit.xml.dist deleted file mode 100644 index 02f183c33e..0000000000 --- a/drop-ins/wp-memcached/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ - - - - ./tests/ - - - - - - ./object-cache.php - - - - - - - diff --git a/drop-ins/wp-memcached/psalm.xml.dist b/drop-ins/wp-memcached/psalm.xml.dist deleted file mode 100644 index 46cae835c1..0000000000 --- a/drop-ins/wp-memcached/psalm.xml.dist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/drop-ins/wp-memcached/readme.md b/drop-ins/wp-memcached/readme.md deleted file mode 100644 index b4e1fe0bbc..0000000000 --- a/drop-ins/wp-memcached/readme.md +++ /dev/null @@ -1,39 +0,0 @@ -# WP Memcached - -CAUTION: This project is a work in progress. - -This is a fork of https://github.com/Automattic/wp-memcached, but may end up merging back upstream eventually. - -- Adds support for the Memcached PHP extension -- General cleanup, along with types enforcement with Psalm. -- More unit testing, for both Memcache and Memcached extensions. - -## Usage - -1. Install this plugin somewhere in your codebase. -2. Create a file at `wp-content/object-cache.php`, with the contents being just `require_once DIR . '/path/to/wp-cache-memcached/object-cache.php`. - -This plugin aims to have full compatability with the wp-memcached plugin, and you can do those first two steps and call it a day. It will work seemlessly as a replacement. Will even keep using the same cache keys/values that are already stored. - -To additionally start using the Memcached php extension instead: - -1. Ensure the Memcached extension is installed, along with the libmemcached library. -2. Add `define( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION', true );` to your wp-config.php file. - -This will result in effectively a cache flush, after which behavior will resume per usual (though hopefully with better performance and consistency). - -## Development - -``` -composer install - -# Linting / Code standards -composer run-script phpcs -composer run-script phpcs:fix - -# Type checking -composer run-script psalm - -# Unit tests -./bin/test.sh -``` diff --git a/drop-ins/wp-memcached/stubs/memcache-stubs.php b/drop-ins/wp-memcached/stubs/memcache-stubs.php deleted file mode 100644 index ba99c91431..0000000000 --- a/drop-ins/wp-memcached/stubs/memcache-stubs.php +++ /dev/null @@ -1,444 +0,0 @@ - - * Open memcached server connection - * @link https://php.net/manual/en/memcache.connect.php - * @param string $host

- * Point to the host where memcached is listening for connections. This parameter - * may also specify other transports like unix:///path/to/memcached.sock - * to use UNIX domain sockets, in this case port must also - * be set to 0. - *

- * @param int $port [optional]

- * Point to the port where memcached is listening for connections. Set this - * parameter to 0 when using UNIX domain sockets. - *

- *

- * Please note: port defaults to - * {@link https://php.net/manual/en/memcache.ini.php#ini.memcache.default-port memcache.default_port} - * if not specified. For this reason it is wise to specify the port - * explicitly in this method call. - *

- * @param int $timeout [optional]

Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.

- * @return bool

Returns TRUE on success or FALSE on failure.

- */ - public function connect($host, $port, $timeout = 1) {} - - /** - * (PECL memcache >= 2.0.0)
- * Add a memcached server to connection pool - * @link https://php.net/manual/en/memcache.addserver.php - * @param string $host

- * Point to the host where memcached is listening for connections. This parameter - * may also specify other transports like unix:///path/to/memcached.sock - * to use UNIX domain sockets, in this case port must also - * be set to 0. - *

- * @param int $port [optional]

- * Point to the port where memcached is listening for connections. - * Set this - * parameter to 0 when using UNIX domain sockets. - *

- *

- * Please note: port defaults to - * memcache.default_port - * if not specified. For this reason it is wise to specify the port - * explicitly in this method call. - *

- * @param bool $persistent [optional]

- * Controls the use of a persistent connection. Default to TRUE. - *

- * @param int $weight [optional]

- * Number of buckets to create for this server which in turn control its - * probability of it being selected. The probability is relative to the - * total weight of all servers. - *

- * @param int $timeout [optional]

- * Value in seconds which will be used for connecting to the daemon. Think - * twice before changing the default value of 1 second - you can lose all - * the advantages of caching if your connection is too slow. - *

- * @param int $retry_interval [optional]

- * Controls how often a failed server will be retried, the default value - * is 15 seconds. Setting this parameter to -1 disables automatic retry. - * Neither this nor the persistent parameter has any - * effect when the extension is loaded dynamically via dl. - *

- *

- * Each failed connection struct has its own timeout and before it has expired - * the struct will be skipped when selecting backends to serve a request. Once - * expired the connection will be successfully reconnected or marked as failed - * for another retry_interval seconds. The typical - * effect is that each web server child will retry the connection about every - * retry_interval seconds when serving a page. - *

- * @param bool $status [optional]

- * Controls if the server should be flagged as online. Setting this parameter - * to FALSE and retry_interval to -1 allows a failed - * server to be kept in the pool so as not to affect the key distribution - * algorithm. Requests for this server will then failover or fail immediately - * depending on the memcache.allow_failover setting. - * Default to TRUE, meaning the server should be considered online. - *

- * @param callable $failure_callback [optional]

- * Allows the user to specify a callback function to run upon encountering an - * error. The callback is run before failover is attempted. The function takes - * two parameters, the hostname and port of the failed server. - *

- * @param int $timeoutms [optional]

- *

- * @return bool TRUE on success or FALSE on failure. - */ - public function addServer($host, $port = 11211, $persistent = true, $weight = null, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null, $timeoutms = null) {} - - /** - * (PECL memcache >= 2.1.0)
- * Changes server parameters and status at runtime - * @link https://secure.php.net/manual/en/memcache.setserverparams.php - * @param string $host

Point to the host where memcached is listening for connections.

. - * @param int $port [optional]

- * Point to the port where memcached is listening for connections. - *

- * @param int $timeout [optional]

- * Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. - *

- * @param int $retry_interval [optional]

- * Controls how often a failed server will be retried, the default value - * is 15 seconds. Setting this parameter to -1 disables automatic retry. - * Neither this nor the persistent parameter has any - * effect when the extension is loaded dynamically via {@link https://secure.php.net/manual/en/function.dl.php dl()}. - *

- * @param bool $status [optional]

- * Controls if the server should be flagged as online. Setting this parameter - * to FALSE and retry_interval to -1 allows a failed - * server to be kept in the pool so as not to affect the key distribution - * algorithm. Requests for this server will then failover or fail immediately - * depending on the memcache.allow_failover setting. - * Default to TRUE, meaning the server should be considered online. - *

- * @param callable $failure_callback [optional]

- * Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. - * The function takes two parameters, the hostname and port of the failed server. - *

- * @return bool

Returns TRUE on success or FALSE on failure.

- */ - public function setServerParams($host, $port = 11211, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null) {} - - public function setFailureCallback() {} - - /** - * (PECL memcache >= 2.1.0)
- * Returns server status - * @link https://php.net/manual/en/memcache.getserverstatus.php - * @param string $host Point to the host where memcached is listening for connections. - * @param int $port Point to the port where memcached is listening for connections. - * @return int Returns a the servers status. 0 if server is failed, non-zero otherwise - */ - public function getServerStatus($host, $port = 11211) {} - - public function findServer() {} - - /** - * (PECL memcache >= 0.2.0)
- * Return version of the server - * @link https://php.net/manual/en/memcache.getversion.php - * @return string|false Returns a string of server version number or FALSE on failure. - */ - public function getVersion() {} - - /** - * (PECL memcache >= 2.0.0)
- * Add an item to the server. If the key already exists, the value will not be added and FALSE will be returned. - * @link https://php.net/manual/en/memcache.add.php - * @param string $key The key that will be associated with the item. - * @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized. - * @param int $flag [optional]

- * Use MEMCACHE_COMPRESSED to store the item - * compressed (uses zlib). - *

- * @param int $expire [optional]

Expiration time of the item. - * If it's equal to zero, the item will never expire. - * You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).

- * @return bool Returns TRUE on success or FALSE on failure. Returns FALSE if such key already exist. For the rest Memcache::add() behaves similarly to Memcache::set(). - */ - public function add($key, $var, $flag = null, $expire = null) {} - - /** - * (PECL memcache >= 0.2.0)
- * Stores an item var with key on the memcached server. Parameter expire is expiration time in seconds. - * If it's 0, the item never expires (but memcached server doesn't guarantee this item to be stored all the time, - * it could be deleted from the cache to make place for other items). - * You can use MEMCACHE_COMPRESSED constant as flag value if you want to use on-the-fly compression (uses zlib). - * @link https://php.net/manual/en/memcache.set.php - * @param string $key The key that will be associated with the item. - * @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized. - * @param int $flag [optional] Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib). - * @param int $expire [optional] Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days). - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function set($key, $var, $flag = null, $expire = null) {} - - /** - * (PECL memcache >= 0.2.0)
- * Replace value of the existing item - * @link https://php.net/manual/en/memcache.replace.php - * @param string $key

The key that will be associated with the item.

- * @param mixed $var

The variable to store. Strings and integers are stored as is, other types are stored serialized.

- * @param int $flag [optional]

Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib).

- * @param int $expire [optional]

Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).

- * @return bool Returns TRUE on success or FALSE on failure. - */ - public function replace($key, $var, $flag = null, $expire = null) {} - - public function cas() {} - - public function append() {} - - /** - * @return string - */ - public function prepend() {} - - /** - * (PECL memcache >= 0.2.0)
- * Retrieve item from the server - * @link https://php.net/manual/en/memcache.get.php - * @param string|array $key

- * The key or array of keys to fetch. - *

- * @param int|array &$flags [optional]

- * If present, flags fetched along with the values will be written to this parameter. These - * flags are the same as the ones given to for example {@link https://php.net/manual/en/memcache.set.php Memcache::set()}. - * The lowest byte of the int is reserved for pecl/memcache internal usage (e.g. to indicate - * compression and serialization status). - *

- * @return string|array|false

- * Returns the string associated with the key or - * an array of found key-value pairs when key is an {@link https://php.net/manual/en/language.types.array.php array}. - * Returns FALSE on failure, key is not found or - * key is an empty {@link https://php.net/manual/en/language.types.array.php array}. - *

- */ - public function get($key, &$flags = null) {} - - /** - * (PECL memcache >= 0.2.0)
- * Delete item from the server - * https://secure.php.net/manual/en/memcache.delete.php - * @param string $key The key associated with the item to delete. - * @param int $timeout [optional] This deprecated parameter is not supported, and defaults to 0 seconds. Do not use this parameter. - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function delete($key, $timeout = 0) {} - - /** - * (PECL memcache >= 0.2.0)
- * Get statistics of the server - * @link https://php.net/manual/en/memcache.getstats.php - * @param string $type [optional]

- * The type of statistics to fetch. - * Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. - * According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".

- * @param int $slabid [optional]

- * Used in conjunction with type set to - * cachedump to identify the slab to dump from. The cachedump - * command ties up the server and is strictly to be used for - * debugging purposes. - *

- * @param int $limit [optional]

- * Used in conjunction with type set to cachedump to limit the number of entries to dump. - *

- * @return array|false Returns an associative array of server statistics or FALSE on failure. - */ - public function getStats($type = null, $slabid = null, $limit = 100) {} - - /** - * (PECL memcache >= 2.0.0)
- * Get statistics from all servers in pool - * @link https://php.net/manual/en/memcache.getextendedstats.php - * @param string $type [optional]

The type of statistics to fetch. Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".

- * @param int $slabid [optional]

- * Used in conjunction with type set to - * cachedump to identify the slab to dump from. The cachedump - * command ties up the server and is strictly to be used for - * debugging purposes. - *

- * @param int $limit Used in conjunction with type set to cachedump to limit the number of entries to dump. - * @return array|false Returns a two-dimensional associative array of server statistics or FALSE - * Returns a two-dimensional associative array of server statistics or FALSE - * on failure. - */ - public function getExtendedStats($type = null, $slabid = null, $limit = 100) {} - - /** - * (PECL memcache >= 2.0.0)
- * Enable automatic compression of large values - * @link https://php.net/manual/en/memcache.setcompressthreshold.php - * @param int $thresold

Controls the minimum value length before attempting to compress automatically.

- * @param float $min_saving [optional]

Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings.

- * @return bool Returns TRUE on success or FALSE on failure. - */ - public function setCompressThreshold($thresold, $min_saving = 0.2) {} - - /** - * (PECL memcache >= 0.2.0)
- * Increment item's value - * @link https://php.net/manual/en/memcache.increment.php - * @param string $key Key of the item to increment. - * @param int $value [optional] increment the item by value - * @return int|false Returns new items value on success or FALSE on failure. - */ - public function increment($key, $value = 1) {} - - /** - * (PECL memcache >= 0.2.0)
- * Decrement item's value - * @link https://php.net/manual/en/memcache.decrement.php - * @param string $key Key of the item do decrement. - * @param int $value Decrement the item by value. - * @return int|false Returns item's new value on success or FALSE on failure. - */ - public function decrement($key, $value = 1) {} - - /** - * (PECL memcache >= 0.4.0)
- * Close memcached server connection - * @link https://php.net/manual/en/memcache.close.php - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function close() {} - - /** - * (PECL memcache >= 1.0.0)
- * Flush all existing items at the server - * @link https://php.net/manual/en/memcache.flush.php - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function flush() {} -} - -/** - * Represents a connection to a set of memcache servers. - * @link https://php.net/manual/en/class.memcache.php - */ -class Memcache extends MemcachePool -{ - /** - * (PECL memcache >= 0.4.0)
- * Open memcached server persistent connection - * @link https://php.net/manual/en/memcache.pconnect.php - * @param string $host

- * Point to the host where memcached is listening for connections. This parameter - * may also specify other transports like unix:///path/to/memcached.sock - * to use UNIX domain sockets, in this case port must also - * be set to 0. - *

- * @param int $port [optional]

- * Point to the port where memcached is listening for connections. Set this - * parameter to 0 when using UNIX domain sockets. - *

- * @param int $timeout [optional]

- * Value in seconds which will be used for connecting to the daemon. Think - * twice before changing the default value of 1 second - you can lose all - * the advantages of caching if your connection is too slow. - *

- * @return mixed a Memcache object or FALSE on failure. - */ - public function pconnect($host, $port, $timeout = 1) {} -} - -// string $host [, int $port [, int $timeout ]] - -/** - * (PECL memcache >= 0.2.0)
- * Memcache::connect — Open memcached server connection - * @link https://php.net/manual/en/memcache.connect.php - * @param string $host

- * Point to the host where memcached is listening for connections. - * This parameter may also specify other transports like - * unix:///path/to/memcached.sock to use UNIX domain sockets, - * in this case port must also be set to 0. - *

- * @param int $port [optional]

- * Point to the port where memcached is listening for connections. - * Set this parameter to 0 when using UNIX domain sockets. - * Note: port defaults to memcache.default_port if not specified. - * For this reason it is wise to specify the port explicitly in this method call. - *

- * @param int $timeout [optional]

- * Value in seconds which will be used for connecting to the daemon. - *

- * @return bool Returns TRUE on success or FALSE on failure. - */ -function memcache_connect($host, $port, $timeout = 1) {} - -/** - * (PECL memcache >= 0.4.0) - * Memcache::pconnect — Open memcached server persistent connection - * - * @link https://php.net/manual/en/memcache.pconnect.php#example-5242 - * @param string $host - * @param int|null $port - * @param int $timeout - * @return Memcache - */ -function memcache_pconnect($host, $port = null, $timeout = 1) {} - -function memcache_add_server() {} - -function memcache_set_server_params() {} - -function memcache_set_failure_callback() {} - -function memcache_get_server_status() {} - -function memcache_get_version() {} - -function memcache_add() {} - -function memcache_set() {} - -function memcache_replace() {} - -function memcache_cas() {} - -function memcache_append() {} - -function memcache_prepend() {} - -function memcache_get() {} - -function memcache_delete() {} - -/** - * (PECL memcache >= 0.2.0)
- * Turn debug output on/off - * @link https://php.net/manual/en/function.memcache-debug.php - * @param bool $on_off

- * Turns debug output on if equals to TRUE. - * Turns debug output off if equals to FALSE. - *

- * @return bool TRUE if PHP was built with --enable-debug option, otherwise - * returns FALSE. - */ -function memcache_debug($on_off) {} - -function memcache_get_stats() {} - -function memcache_get_extended_stats() {} - -function memcache_set_compress_threshold() {} - -function memcache_increment() {} - -function memcache_decrement() {} - -function memcache_close() {} - -function memcache_flush() {} - -// End of memcache v.3.0.8 diff --git a/drop-ins/wp-memcached/stubs/memcached-stubs.php b/drop-ins/wp-memcached/stubs/memcached-stubs.php deleted file mode 100644 index b87387aa1e..0000000000 --- a/drop-ins/wp-memcached/stubs/memcached-stubs.php +++ /dev/null @@ -1,1528 +0,0 @@ -Enables or disables payload compression. When enabled, - * item values longer than a certain threshold (currently 100 bytes) will be - * compressed during storage and decompressed during retrieval - * transparently.

- *

Type: boolean, default: TRUE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_COMPRESSION = -1001; - const OPT_COMPRESSION_TYPE = -1004; - - /** - *

This can be used to create a "domain" for your item keys. The value - * specified here will be prefixed to each of the keys. It cannot be - * longer than 128 characters and will reduce the - * maximum available key size. The prefix is applied only to the item keys, - * not to the server keys.

- *

Type: string, default: "".

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_PREFIX_KEY = -1002; - - /** - *

- * Specifies the serializer to use for serializing non-scalar values. - * The valid serializers are Memcached::SERIALIZER_PHP - * or Memcached::SERIALIZER_IGBINARY. The latter is - * supported only when memcached is configured with - * --enable-memcached-igbinary option and the - * igbinary extension is loaded. - *

- *

Type: integer, default: Memcached::SERIALIZER_PHP.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SERIALIZER = -1003; - - /** - *

Indicates whether igbinary serializer support is available.

- *

Type: boolean.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_IGBINARY = 0; - - /** - *

Indicates whether JSON serializer support is available.

- *

Type: boolean.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_JSON = 0; - - /** - *

Indicates whether msgpack serializer support is available.

- *

Type: boolean.

- * Available as of Memcached 3.0.0. - * @since 3.0.0 - * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_MSGPACK = 0; - - /** - *

Indicate whether set_encoding_key is available

- *

Type: boolean.

- * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php, https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c#L4387 - */ - const HAVE_ENCODING = 0; - - /** - * Feature support - */ - const HAVE_SESSION = 1; - const HAVE_SASL = 0; - - /** - *

Specifies the hashing algorithm used for the item keys. The valid - * values are supplied via Memcached::HASH_* constants. - * Each hash algorithm has its advantages and its disadvantages. Go with the - * default if you don't know or don't care.

- *

Type: integer, default: Memcached::HASH_DEFAULT

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_HASH = 2; - - /** - *

The default (Jenkins one-at-a-time) item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_DEFAULT = 0; - - /** - *

MD5 item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_MD5 = 1; - - /** - *

CRC item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_CRC = 2; - - /** - *

FNV1_64 item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1_64 = 3; - - /** - *

FNV1_64A item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1A_64 = 4; - - /** - *

FNV1_32 item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1_32 = 5; - - /** - *

FNV1_32A item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1A_32 = 6; - - /** - *

Hsieh item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_HSIEH = 7; - - /** - *

Murmur item key hashing algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_MURMUR = 8; - - /** - *

Specifies the method of distributing item keys to the servers. - * Currently supported methods are modulo and consistent hashing. Consistent - * hashing delivers better distribution and allows servers to be added to - * the cluster with minimal cache losses.

- *

Type: integer, default: Memcached::DISTRIBUTION_MODULA.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_DISTRIBUTION = 9; - - /** - *

Modulo-based key distribution algorithm.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const DISTRIBUTION_MODULA = 0; - - /** - *

Consistent hashing key distribution algorithm (based on libketama).

- * @link https://php.net/manual/en/memcached.constants.php - */ - const DISTRIBUTION_CONSISTENT = 1; - const DISTRIBUTION_VIRTUAL_BUCKET = 6; - - /** - *

Enables or disables compatibility with libketama-like behavior. When - * enabled, the item key hashing algorithm is set to MD5 and distribution is - * set to be weighted consistent hashing distribution. This is useful - * because other libketama-based clients (Python, Ruby, etc.) with the same - * server configuration will be able to access the keys transparently. - *

- *

- * It is highly recommended to enable this option if you want to use - * consistent hashing, and it may be enabled by default in future - * releases. - *

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_LIBKETAMA_COMPATIBLE = 16; - const OPT_LIBKETAMA_HASH = 17; - const OPT_TCP_KEEPALIVE = 32; - - /** - *

Enables or disables buffered I/O. Enabling buffered I/O causes - * storage commands to "buffer" instead of being sent. Any action that - * retrieves data causes this buffer to be sent to the remote connection. - * Quitting the connection or closing down the connection will also cause - * the buffered data to be pushed to the remote connection.

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_BUFFER_WRITES = 10; - - /** - *

Enable the use of the binary protocol. Please note that you cannot - * toggle this option on an open connection.

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_BINARY_PROTOCOL = 18; - - /** - *

Enables or disables asynchronous I/O. This is the fastest transport - * available for storage functions.

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_NO_BLOCK = 0; - - /** - *

Enables or disables the no-delay feature for connecting sockets (may - * be faster in some environments).

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_TCP_NODELAY = 1; - - /** - *

The maximum socket send buffer in bytes.

- *

Type: integer, default: varies by platform/kernel - * configuration.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SOCKET_SEND_SIZE = 4; - - /** - *

The maximum socket receive buffer in bytes.

- *

Type: integer, default: varies by platform/kernel - * configuration.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SOCKET_RECV_SIZE = 5; - - /** - *

In non-blocking mode this set the value of the timeout during socket - * connection, in milliseconds.

- *

Type: integer, default: 1000.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_CONNECT_TIMEOUT = 14; - - /** - *

The amount of time, in seconds, to wait until retrying a failed - * connection attempt.

- *

Type: integer, default: 0.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_RETRY_TIMEOUT = 15; - - /** - *

Socket sending timeout, in microseconds. In cases where you cannot - * use non-blocking I/O this will allow you to still have timeouts on the - * sending of data.

- *

Type: integer, default: 0.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SEND_TIMEOUT = 19; - - /** - *

Socket reading timeout, in microseconds. In cases where you cannot - * use non-blocking I/O this will allow you to still have timeouts on the - * reading of data.

- *

Type: integer, default: 0.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_RECV_TIMEOUT = 20; - - /** - *

Timeout for connection polling, in milliseconds.

- *

Type: integer, default: 1000.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_POLL_TIMEOUT = 8; - - /** - *

Enables or disables caching of DNS lookups.

- *

Type: boolean, default: FALSE.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_CACHE_LOOKUPS = 6; - - /** - *

Specifies the failure limit for server connection attempts. The - * server will be removed after this many continuous connection - * failures.

- *

Type: integer, default: 0.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SERVER_FAILURE_LIMIT = 21; - const OPT_AUTO_EJECT_HOSTS = 28; - const OPT_HASH_WITH_PREFIX_KEY = 25; - const OPT_NOREPLY = 26; - const OPT_SORT_HOSTS = 12; - const OPT_VERIFY_KEY = 13; - const OPT_USE_UDP = 27; - const OPT_NUMBER_OF_REPLICAS = 29; - const OPT_RANDOMIZE_REPLICA_READ = 30; - const OPT_CORK = 31; - const OPT_REMOVE_FAILED_SERVERS = 35; - const OPT_DEAD_TIMEOUT = 36; - const OPT_SERVER_TIMEOUT_LIMIT = 37; - const OPT_MAX = 38; - const OPT_IO_BYTES_WATERMARK = 23; - const OPT_IO_KEY_PREFETCH = 24; - const OPT_IO_MSG_WATERMARK = 22; - const OPT_LOAD_FROM_FILE = 34; - const OPT_SUPPORT_CAS = 7; - const OPT_TCP_KEEPIDLE = 33; - const OPT_USER_DATA = 11; - - - /** - * libmemcached result codes - */ - /** - *

The operation was successful.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SUCCESS = 0; - - /** - *

The operation failed in some fashion.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_FAILURE = 1; - - /** - *

DNS lookup failed.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_HOST_LOOKUP_FAILURE = 2; - - /** - *

Failed to read network data.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_UNKNOWN_READ_FAILURE = 7; - - /** - *

Bad command in memcached protocol.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PROTOCOL_ERROR = 8; - - /** - *

Error on the client side.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_CLIENT_ERROR = 9; - - /** - *

Error on the server side.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SERVER_ERROR = 10; - - /** - *

Failed to write network data.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_WRITE_FAILURE = 5; - - /** - *

Failed to do compare-and-swap: item you are trying to store has been - * modified since you last fetched it.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_DATA_EXISTS = 12; - - /** - *

Item was not stored: but not because of an error. This normally - * means that either the condition for an "add" or a "replace" command - * wasn't met, or that the item is in a delete queue.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NOTSTORED = 14; - - /** - *

Item with this key was not found (with "get" operation or "cas" - * operations).

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NOTFOUND = 16; - - /** - *

Partial network data read error.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PARTIAL_READ = 18; - - /** - *

Some errors occurred during multi-get.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SOME_ERRORS = 19; - - /** - *

Server list is empty.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NO_SERVERS = 20; - - /** - *

End of result set.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_END = 21; - - /** - *

System error.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_ERRNO = 26; - - /** - *

The operation was buffered.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_BUFFERED = 32; - - /** - *

The operation timed out.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_TIMEOUT = 31; - - /** - *

Bad key.

- * @link https://php.net/manual/en/memcached.constants.php, http://docs.libmemcached.org/index.html - */ - /** - *

MEMCACHED_BAD_KEY_PROVIDED: The key provided is not a valid key.

- */ - const RES_BAD_KEY_PROVIDED = 33; - /** - *

MEMCACHED_STORED: The requested object has been successfully stored on the server.

- */ - const RES_STORED = 15; - /** - *

MEMCACHED_DELETED: The object requested by the key has been deleted.

- */ - const RES_DELETED = 22; - /** - *

MEMCACHED_STAT: A “stat” command has been returned in the protocol.

- */ - const RES_STAT = 24; - /** - *

MEMCACHED_ITEM: An item has been fetched (this is an internal error only).

- */ - const RES_ITEM = 25; - /** - *

MEMCACHED_NOT_SUPPORTED: The given method is not supported in the server.

- */ - const RES_NOT_SUPPORTED = 28; - /** - *

MEMCACHED_FETCH_NOTFINISHED: A request has been made, but the server has not finished the fetch of the last request.

- */ - const RES_FETCH_NOTFINISHED = 30; - /** - *

MEMCACHED_SERVER_MARKED_DEAD: The requested server has been marked dead.

- */ - const RES_SERVER_MARKED_DEAD = 35; - /** - *

MEMCACHED_UNKNOWN_STAT_KEY: The server you are communicating with has a stat key which has not be defined in the protocol.

- */ - const RES_UNKNOWN_STAT_KEY = 36; - /** - *

MEMCACHED_INVALID_HOST_PROTOCOL: The server you are connecting too has an invalid protocol. Most likely you are connecting to an older server that does not speak the binary protocol.

- */ - const RES_INVALID_HOST_PROTOCOL = 34; - /** - *

MEMCACHED_MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory.

- */ - const RES_MEMORY_ALLOCATION_FAILURE = 17; - /** - *

MEMCACHED_E2BIG: Item is too large for the server to store.

- */ - const RES_E2BIG = 37; - /** - *

MEMCACHED_KEY_TOO_BIG: The key that has been provided is too large for the given server.

- */ - const RES_KEY_TOO_BIG = 39; - /** - *

MEMCACHED_SERVER_TEMPORARILY_DISABLED

- */ - const RES_SERVER_TEMPORARILY_DISABLED = 47; - /** - *

MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory. - * - * #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000008

- */ - const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48; - /** - *

MEMCACHED_AUTH_PROBLEM: An unknown issue has occured during authentication.

- */ - const RES_AUTH_PROBLEM = 40; - /** - *

MEMCACHED_AUTH_FAILURE: The credentials provided are not valid for this server.

- */ - const RES_AUTH_FAILURE = 41; - /** - *

MEMCACHED_AUTH_CONTINUE: Authentication has been paused.

- */ - const RES_AUTH_CONTINUE = 42; - /** - *

MEMCACHED_CONNECTION_FAILURE: A unknown error has occured while trying to connect to a server.

- */ - const RES_CONNECTION_FAILURE = 3; - /** - *

MEMCACHED_CONNECTION_BIND_FAILURE: Deprecated since version <0.30(libmemcached). - * We were not able to bind() to the socket.

- */ - const RES_CONNECTION_BIND_FAILURE = 4; - /** - *

MEMCACHED_READ_FAILURE: A read failure has occurred.

- */ - const RES_READ_FAILURE = 6; - /** - *

MEMCACHED_DATA_DOES_NOT_EXIST: The data requested with the key given was not found.

- */ - const RES_DATA_DOES_NOT_EXIST = 13; - /** - *

MEMCACHED_VALUE: A value has been returned from the server (this is an internal condition only).

- */ - const RES_VALUE = 23; - /** - *

MEMCACHED_FAIL_UNIX_SOCKET: A connection was not established with the server via a unix domain socket.

- */ - const RES_FAIL_UNIX_SOCKET = 27; - /** - *

MEMCACHED_NO_KEY_PROVIDED: Deprecated since version <0.30(libmemcached): Use MEMCACHED_BAD_KEY_PROVIDED instead. - * No key was provided.

- */ - const RES_NO_KEY_PROVIDED = 29; - /** - *

MEMCACHED_INVALID_ARGUMENTS: The arguments supplied to the given function were not valid.

- */ - const RES_INVALID_ARGUMENTS = 38; - /** - *

MEMCACHED_PARSE_ERROR: An error has occurred while trying to parse the configuration string. You should use memparse to determine what the error was.

- */ - const RES_PARSE_ERROR = 43; - /** - *

MEMCACHED_PARSE_USER_ERROR: An error has occurred in parsing the configuration string.

- */ - const RES_PARSE_USER_ERROR = 44; - /** - *

MEMCACHED_DEPRECATED: The method that was requested has been deprecated.

- */ - const RES_DEPRECATED = 45; - //unknow - const RES_IN_PROGRESS = 46; - /** - *

MEMCACHED_MAXIMUM_RETURN: This in an internal only state.

- */ - const RES_MAXIMUM_RETURN = 49; - - /** - * Server callbacks, if compiled with --memcached-protocol - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const ON_CONNECT = 0; - const ON_ADD = 1; - const ON_APPEND = 2; - const ON_DECREMENT = 3; - const ON_DELETE = 4; - const ON_FLUSH = 5; - const ON_GET = 6; - const ON_INCREMENT = 7; - const ON_NOOP = 8; - const ON_PREPEND = 9; - const ON_QUIT = 10; - const ON_REPLACE = 11; - const ON_SET = 12; - const ON_STAT = 13; - const ON_VERSION = 14; - /** - * Constants used when compiled with --memcached-protocol - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const RESPONSE_SUCCESS = 0; - const RESPONSE_KEY_ENOENT = 1; - const RESPONSE_KEY_EEXISTS = 2; - const RESPONSE_E2BIG = 3; - const RESPONSE_EINVAL = 4; - const RESPONSE_NOT_STORED = 5; - const RESPONSE_DELTA_BADVAL = 6; - const RESPONSE_NOT_MY_VBUCKET = 7; - const RESPONSE_AUTH_ERROR = 32; - const RESPONSE_AUTH_CONTINUE = 33; - const RESPONSE_UNKNOWN_COMMAND = 129; - const RESPONSE_ENOMEM = 130; - const RESPONSE_NOT_SUPPORTED = 131; - const RESPONSE_EINTERNAL = 132; - const RESPONSE_EBUSY = 133; - const RESPONSE_ETMPFAIL = 134; - - - /** - *

Failed to create network socket.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11; - - /** - *

Payload failure: could not compress/decompress or serialize/unserialize the value.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PAYLOAD_FAILURE = -1001; - - /** - *

The default PHP serializer.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_PHP = 1; - - /** - *

The igbinary serializer. - * Instead of textual representation it stores PHP data structures in a - * compact binary form, resulting in space and time gains.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_IGBINARY = 2; - - /** - *

The JSON serializer. Requires PHP 5.2.10+.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_JSON = 3; - const SERIALIZER_JSON_ARRAY = 4; - /** - *

The msgpack serializer.

- * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const SERIALIZER_MSGPACK = 5; - - const COMPRESSION_FASTLZ = 2; - const COMPRESSION_ZLIB = 1; - - /** - *

A flag for Memcached::getMulti and - * Memcached::getMultiByKey to ensure that the keys are - * returned in the same order as they were requested in. Non-existing keys - * get a default value of NULL.

- * @link https://php.net/manual/en/memcached.constants.php - */ - const GET_PRESERVE_ORDER = 1; - - /** - * A flag for Memcached::get(), Memcached::getMulti() and - * Memcached::getMultiByKey() to ensure that the CAS token values are returned as well. - * @link https://php.net/manual/en/memcached.constants.php - */ - const GET_EXTENDED = 2; - - const GET_ERROR_RETURN_VALUE = false; - - /** - * (PECL memcached >= 0.1.0)
- * Create a Memcached instance - * @link https://php.net/manual/en/memcached.construct.php, https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @param string $persistent_id [optional] - * @param callable $on_new_object_cb [optional] - * @param string $connection_str [optional] - */ - public function __construct ($persistent_id = '', $on_new_object_cb = null, $connection_str = '') {} - - /** - * (PECL memcached >= 0.1.0)
- * Return the result code of the last operation - * @link https://php.net/manual/en/memcached.getresultcode.php - * @return int Result code of the last Memcached operation. - */ - public function getResultCode () {} - - /** - * (PECL memcached >= 1.0.0)
- * Return the message describing the result of the last operation - * @link https://php.net/manual/en/memcached.getresultmessage.php - * @return string Message describing the result of the last Memcached operation. - */ - public function getResultMessage () {} - - /** - * (PECL memcached >= 0.1.0)
- * Retrieve an item - * @link https://php.net/manual/en/memcached.get.php - * @param string $key

- * The key of the item to retrieve. - *

- * @param callable $cache_cb [optional]

- * Read-through caching callback or NULL. - *

- * @param int $flags [optional]

- * The flags for the get operation. - *

- * @return mixed the value stored in the cache or FALSE otherwise. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function get ($key, callable $cache_cb = null, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Retrieve an item from a specific server - * @link https://php.net/manual/en/memcached.getbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key of the item to fetch. - *

- * @param callable $cache_cb [optional]

- * Read-through caching callback or NULL - *

- * @param int $flags [optional]

- * The flags for the get operation. - *

- * @return mixed the value stored in the cache or FALSE otherwise. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function getByKey ($server_key, $key, callable $cache_cb = null, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Retrieve multiple items - * @link https://php.net/manual/en/memcached.getmulti.php - * @param array $keys

- * Array of keys to retrieve. - *

- * @param int $flags [optional]

- * The flags for the get operation. - *

- * @return mixed the array of found items or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getMulti (array $keys, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Retrieve multiple items from a specific server - * @link https://php.net/manual/en/memcached.getmultibykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param array $keys

- * Array of keys to retrieve. - *

- * @param int $flags [optional]

- * The flags for the get operation. - *

- * @return array|false the array of found items or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getMultiByKey ($server_key, array $keys, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Request multiple items - * @link https://php.net/manual/en/memcached.getdelayed.php - * @param array $keys

- * Array of keys to request. - *

- * @param bool $with_cas [optional]

- * Whether to request CAS token values also. - *

- * @param callable $value_cb [optional]

- * The result callback or NULL. - *

- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getDelayed (array $keys, $with_cas = null, callable $value_cb = null) {} - - /** - * (PECL memcached >= 0.1.0)
- * Request multiple items from a specific server - * @link https://php.net/manual/en/memcached.getdelayedbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param array $keys

- * Array of keys to request. - *

- * @param bool $with_cas [optional]

- * Whether to request CAS token values also. - *

- * @param callable $value_cb [optional]

- * The result callback or NULL. - *

- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getDelayedByKey ($server_key, array $keys, $with_cas = null, callable $value_cb = null) {} - - /** - * (PECL memcached >= 0.1.0)
- * Fetch the next result - * @link https://php.net/manual/en/memcached.fetch.php - * @return array|false the next result or FALSE otherwise. - * The Memcached::getResultCode will return - * Memcached::RES_END if result set is exhausted. - */ - public function fetch () {} - - /** - * (PECL memcached >= 0.1.0)
- * Fetch all the remaining results - * @link https://php.net/manual/en/memcached.fetchall.php - * @return array|false the results or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function fetchAll () {} - - /** - * (PECL memcached >= 0.1.0)
- * Store an item - * @link https://php.net/manual/en/memcached.set.php - * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function set ($key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Store an item on a specific server - * @link https://php.net/manual/en/memcached.setbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Set a new expiration on an item - * @link https://php.net/manual/en/memcached.touch.php - * @param string $key

- * The key under which to store the value. - *

- * @param int $expiration

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function touch ($key, $expiration = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Set a new expiration on an item on a specific server - * @link https://php.net/manual/en/memcached.touchbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param int $expiration

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function touchByKey ($server_key, $key, $expiration) {} - - /** - * (PECL memcached >= 0.1.0)
- * Store multiple items - * @link https://php.net/manual/en/memcached.setmulti.php - * @param array $items

- * An array of key/value pairs to store on the server. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setMulti (array $items, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Store multiple items on a specific server - * @link https://php.net/manual/en/memcached.setmultibykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param array $items

- * An array of key/value pairs to store on the server. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setMultiByKey ($server_key, array $items, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Compare and swap an item - * @link https://php.net/manual/en/memcached.cas.php - * @param float $cas_token

- * Unique value associated with the existing item. Generated by memcache. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_DATA_EXISTS if the item you are trying - * to store has been modified since you last fetched it. - */ - public function cas ($cas_token, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Compare and swap an item on a specific server - * @link https://php.net/manual/en/memcached.casbykey.php - * @param float $cas_token

- * Unique value associated with the existing item. Generated by memcache. - *

- * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_DATA_EXISTS if the item you are trying - * to store has been modified since you last fetched it. - */ - public function casByKey ($cas_token, $server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Add an item under a new key - * @link https://php.net/manual/en/memcached.add.php - * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key already exists. - */ - public function add ($key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Add an item under a new key on a specific server - * @link https://php.net/manual/en/memcached.addbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key already exists. - */ - public function addByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Append data to an existing item - * @link https://php.net/manual/en/memcached.append.php - * @param string $key

- * The key under which to store the value. - *

- * @param string $value

- * The string to append. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function append ($key, $value) {} - - /** - * (PECL memcached >= 0.1.0)
- * Append data to an existing item on a specific server - * @link https://php.net/manual/en/memcached.appendbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param string $value

- * The string to append. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function appendByKey ($server_key, $key, $value) {} - - /** - * (PECL memcached >= 0.1.0)
- * Prepend data to an existing item - * @link https://php.net/manual/en/memcached.prepend.php - * @param string $key

- * The key of the item to prepend the data to. - *

- * @param string $value

- * The string to prepend. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function prepend ($key, $value) {} - - /** - * (PECL memcached >= 0.1.0)
- * Prepend data to an existing item on a specific server - * @link https://php.net/manual/en/memcached.prependbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key of the item to prepend the data to. - *

- * @param string $value

- * The string to prepend. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function prependByKey ($server_key, $key, $value) {} - - /** - * (PECL memcached >= 0.1.0)
- * Replace the item under an existing key - * @link https://php.net/manual/en/memcached.replace.php - * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function replace ($key, $value, $expiration = null, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Replace the item under an existing key on a specific server - * @link https://php.net/manual/en/memcached.replacebykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key under which to store the value. - *

- * @param mixed $value

- * The value to store. - *

- * @param int $expiration [optional]

- * The expiration time, defaults to 0. See Expiration Times for more info. - *

- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function replaceByKey ($server_key, $key, $value, $expiration = null, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Delete an item - * @link https://php.net/manual/en/memcached.delete.php - * @param string $key

- * The key to be deleted. - *

- * @param int $time [optional]

- * The amount of time the server will wait to delete the item. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function delete ($key, $time = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Delete multiple items - * @link https://php.net/manual/en/memcached.deletemulti.php - * @param array $keys

- * The keys to be deleted. - *

- * @param int $time [optional]

- * The amount of time the server will wait to delete the items. - *

- * @return array Returns array indexed by keys and where values are indicating whether operation succeeded or not. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteMulti (array $keys, $time = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Delete an item from a specific server - * @link https://php.net/manual/en/memcached.deletebykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key to be deleted. - *

- * @param int $time [optional]

- * The amount of time the server will wait to delete the item. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteByKey ($server_key, $key, $time = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Delete multiple items from a specific server - * @link https://php.net/manual/en/memcached.deletemultibykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param array $keys

- * The keys to be deleted. - *

- * @param int $time [optional]

- * The amount of time the server will wait to delete the items. - *

- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteMultiByKey ($server_key, array $keys, $time = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Increment numeric item's value - * @link https://php.net/manual/en/memcached.increment.php - * @param string $key

- * The key of the item to increment. - *

- * @param int $offset [optional]

- * The amount by which to increment the item's value. - *

- * @param int $initial_value [optional]

- * The value to set the item to if it doesn't currently exist. - *

- * @param int $expiry [optional]

- * The expiry time to set on the item. - *

- * @return int|false new item's value on success or FALSE on failure. - */ - public function increment ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Decrement numeric item's value - * @link https://php.net/manual/en/memcached.decrement.php - * @param string $key

- * The key of the item to decrement. - *

- * @param int $offset [optional]

- * The amount by which to decrement the item's value. - *

- * @param int $initial_value [optional]

- * The value to set the item to if it doesn't currently exist. - *

- * @param int $expiry [optional]

- * The expiry time to set on the item. - *

- * @return int|false item's new value on success or FALSE on failure. - */ - public function decrement ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Increment numeric item's value, stored on a specific server - * @link https://php.net/manual/en/memcached.incrementbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key of the item to increment. - *

- * @param int $offset [optional]

- * The amount by which to increment the item's value. - *

- * @param int $initial_value [optional]

- * The value to set the item to if it doesn't currently exist. - *

- * @param int $expiry [optional]

- * The expiry time to set on the item. - *

- * @return int|false new item's value on success or FALSE on failure. - */ - public function incrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 2.0.0)
- * Decrement numeric item's value, stored on a specific server - * @link https://php.net/manual/en/memcached.decrementbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @param string $key

- * The key of the item to decrement. - *

- * @param int $offset [optional]

- * The amount by which to decrement the item's value. - *

- * @param int $initial_value [optional]

- * The value to set the item to if it doesn't currently exist. - *

- * @param int $expiry [optional]

- * The expiry time to set on the item. - *

- * @return int|false item's new value on success or FALSE on failure. - */ - public function decrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Add a server to the server pool - * @link https://php.net/manual/en/memcached.addserver.php - * @param string $host

- * The hostname of the memcache server. If the hostname is invalid, data-related - * operations will set - * Memcached::RES_HOST_LOOKUP_FAILURE result code. - *

- * @param int $port

- * The port on which memcache is running. Usually, this is - * 11211. - *

- * @param int $weight [optional]

- * The weight of the server relative to the total weight of all the - * servers in the pool. This controls the probability of the server being - * selected for operations. This is used only with consistent distribution - * option and usually corresponds to the amount of memory available to - * memcache on that server. - *

- * @return bool TRUE on success or FALSE on failure. - */ - public function addServer ($host, $port, $weight = 0) {} - - /** - * (PECL memcached >= 0.1.1)
- * Add multiple servers to the server pool - * @link https://php.net/manual/en/memcached.addservers.php - * @param array $servers - * @return bool TRUE on success or FALSE on failure. - */ - public function addServers (array $servers) {} - - /** - * (PECL memcached >= 0.1.0)
- * Get the list of the servers in the pool - * @link https://php.net/manual/en/memcached.getserverlist.php - * @return array The list of all servers in the server pool. - */ - public function getServerList () {} - - /** - * (PECL memcached >= 0.1.0)
- * Map a key to a server - * @link https://php.net/manual/en/memcached.getserverbykey.php - * @param string $server_key

- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *

- * @return array an array containing three keys of host, - * port, and weight on success or FALSE - * on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getServerByKey ($server_key) {} - - /** - * (PECL memcached >= 2.0.0)
- * Clears all servers from the server list - * @link https://php.net/manual/en/memcached.resetserverlist.php - * @return bool TRUE on success or FALSE on failure. - */ - public function resetServerList () {} - - /** - * (PECL memcached >= 2.0.0)
- * Close any open connections - * @link https://php.net/manual/en/memcached.quit.php - * @return bool TRUE on success or FALSE on failure. - */ - public function quit () {} - - /** - * (PECL memcached >= 0.1.0)
- * Get server pool statistics - * @link https://php.net/manual/en/memcached.getstats.php - * @param string $type

items, slabs, sizes ...

- * @return array Array of server statistics, one entry per server. - */ - public function getStats ($type = null) {} - - /** - * (PECL memcached >= 0.1.5)
- * Get server pool version info - * @link https://php.net/manual/en/memcached.getversion.php - * @return array Array of server versions, one entry per server. - */ - public function getVersion () {} - - /** - * (PECL memcached >= 2.0.0)
- * Gets the keys stored on all the servers - * @link https://php.net/manual/en/memcached.getallkeys.php - * @return array|false the keys stored on all the servers on success or FALSE on failure. - */ - public function getAllKeys () {} - - /** - * (PECL memcached >= 0.1.0)
- * Invalidate all items in the cache - * @link https://php.net/manual/en/memcached.flush.php - * @param int $delay [optional]

- * Numer of seconds to wait before invalidating the items. - *

- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function flush ($delay = 0) {} - - /** - * (PECL memcached >= 0.1.0)
- * Retrieve a Memcached option value - * @link https://php.net/manual/en/memcached.getoption.php - * @param int $option

- * One of the Memcached::OPT_* constants. - *

- * @return mixed the value of the requested option, or FALSE on - * error. - */ - public function getOption ($option) {} - - /** - * (PECL memcached >= 0.1.0)
- * Set a Memcached option - * @link https://php.net/manual/en/memcached.setoption.php - * @param int $option - * @param mixed $value - * @return bool TRUE on success or FALSE on failure. - */ - public function setOption ($option, $value) {} - - /** - * (PECL memcached >= 2.0.0)
- * Set Memcached options - * @link https://php.net/manual/en/memcached.setoptions.php - * @param array $options

- * An associative array of options where the key is the option to set and - * the value is the new value for the option. - *

- * @return bool TRUE on success or FALSE on failure. - */ - public function setOptions (array $options) {} - - /** - * (PECL memcached >= 2.0.0)
- * Set the credentials to use for authentication - * @link https://www.php.net/manual/en/memcached.setsaslauthdata.php - * @param string $username

- * The username to use for authentication. - *

- * @param string $password

- * The password to use for authentication. - *

- * @return bool TRUE on success or FALSE on failure. - */ - public function setSaslAuthData (string $username , string $password) {} - - /** - * (PECL memcached >= 2.0.0)
- * Check if a persitent connection to memcache is being used - * @link https://php.net/manual/en/memcached.ispersistent.php - * @return bool true if Memcache instance uses a persistent connection, false otherwise. - */ - public function isPersistent () {} - - /** - * (PECL memcached >= 2.0.0)
- * Check if the instance was recently created - * @link https://php.net/manual/en/memcached.ispristine.php - * @return bool the true if instance is recently created, false otherwise. - */ - public function isPristine () {} - - /** - * Flush and send buffered commands - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @return bool - */ - public function flushBuffers () {} - - /** - * Sets AES encryption key (libmemcached 1.0.6 and higher) - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @param string $key - * @return bool - */ - public function setEncodingKey ( $key ) {} - - /** - * Returns the last disconnected server. Was added in 0.34 according to libmemcached's Changelog - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @return array|false - */ - public function getLastDisconnectedServer () {} - - /** - * Returns the last error errno that occurred - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @return int - */ - public function getLastErrorErrno () {} - - /** - * Returns the last error code that occurred - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @return int - */ - public function getLastErrorCode () {} - - /** - * Returns the last error message that occurred - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @return string - */ - public function getLastErrorMessage () {} - - /** - * Sets the memcached virtual buckets - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c - * @param array $host_map - * @param array $forward_map - * @param int $replicas - * @return bool - */ - public function setBucket (array $host_map, array $forward_map, $replicas) {} - -} - -/** - * @link https://php.net/manual/en/class.memcachedexception.php - */ -class MemcachedException extends RuntimeException { - function __construct( $errmsg = "", $errcode = 0 ) {} -} -// End of memcached v.3.1.5 -?> diff --git a/drop-ins/wp-memcached/stubs/wordpress-stubs.php b/drop-ins/wp-memcached/stubs/wordpress-stubs.php deleted file mode 100644 index 5b49aafd81..0000000000 --- a/drop-ins/wp-memcached/stubs/wordpress-stubs.php +++ /dev/null @@ -1,130 +0,0 @@ -`, `&`, and fixes line endings. - * - * Escapes text strings for echoing in JS. It is intended to be used for inline JS - * (in a tag attribute, for example `onclick="..."`). Note that the strings have to - * be in single quotes. The {@see 'js_escape'} filter is also applied here. - * - * @since 2.8.0 - * - * @param string $text The text to be escaped. - * @return string Escaped text. - */ -function esc_js($text) {} diff --git a/drop-ins/wp-memcached/tests/bootstrap.php b/drop-ins/wp-memcached/tests/bootstrap.php deleted file mode 100644 index e9967159a7..0000000000 --- a/drop-ins/wp-memcached/tests/bootstrap.php +++ /dev/null @@ -1,34 +0,0 @@ -object_cache = new WP_Object_Cache(); // phpcs:ignore - - if ( defined( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION' ) && AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION ) { - $this->is_using_memcached_ext = true; - } - } - - public function tearDown(): void { - $this->object_cache->flush(); - $this->object_cache->close(); - parent::tearDown(); - } - - /* - |-------------------------------------------------------------------------- - | The main methods used by the cache API. - |-------------------------------------------------------------------------- - */ - - /** - * @dataProvider data_cache_inputs - */ - public function test_add( $value ) { - // Add to memcached. - self::assertTrue( $this->object_cache->add( 'key', $value ) ); - - // Check local cache. - $cache_key = $this->object_cache->key( 'key', 'default' ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $value, - 'found' => true, - ] ); - - // Fails to add now because it already exists in local cache. - self::assertFalse( $this->object_cache->add( 'key', $value ) ); - - // Still fails after removing from local cache because it exists in memcached. - unset( $this->object_cache->cache[ $cache_key ] ); - self::assertFalse( $this->object_cache->add( 'key', $value ) ); - - // TODO: Test unsetting from local cache after memcached failure. - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_add_for_non_persistent_groups( $value ) { - $group = 'do-not-persist-me'; - - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Add to local cache. - self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); - - // Check local cache. - $cache_key = $this->object_cache->key( 'key', $group ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $value, - 'found' => false, - ] ); - - // Fails to add now because it already exists in local cache. - self::assertFalse( $this->object_cache->add( 'key', $value, $group ) ); - - // Succeeds after removing from local cache because it never existed remotely. - unset( $this->object_cache->cache[ $cache_key ] ); - self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); - } - - public function test_add_multiple() { - $inputs = $this->data_cache_inputs(); - - $values = []; - $expected_first = []; - $expected_second = []; - foreach ( $inputs as $key => $input_array ) { - $values[ $key ] = $input_array[0]; - $expected_first[ $key ] = true; - $expected_second[ $key ] = false; - } - - // Add to memcached. - self::assertEquals( $this->object_cache->add_multiple( $values ), $expected_first ); - - // Check local cache. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $input_array[0], - 'found' => true, - ] ); - } - - // Fails to add all but the new one now because they already exists. - $values['something-new'] = 'test'; - $expected_second['something-new'] = true; - self::assertEquals( $this->object_cache->add_multiple( $values ), $expected_second ); - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_replace( $value, $replace_value ) { - // Add to memcached first. - self::assertTrue( $this->object_cache->add( 'key', $value ) ); - - // Replace with new value. - self::assertTrue( $this->object_cache->replace( 'key', $replace_value ) ); - - // Check local cache. - $cache_key = $this->object_cache->key( 'key', 'default' ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $replace_value, - 'found' => true, - ] ); - - // Can't replace a value that isn't set yet. - self::assertFalse( $this->object_cache->replace( 'new_key', $replace_value ) ); - - // TODO: Test unsetting from local cache after memcached failure. - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_replace_for_non_persistent_groups( $value, $replace_value ) { - $group = 'do-not-persist-me'; - - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Add to memcached first. - self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); - - // Replace with new value. - self::assertTrue( $this->object_cache->replace( 'key', $replace_value, $group ) ); - - // Check local cache. - $cache_key = $this->object_cache->key( 'key', $group ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $replace_value, - 'found' => false, - ] ); - - // Can't replace a value that isn't set yet. - self::assertFalse( $this->object_cache->replace( 'new_key', $replace_value, $group ) ); - - // Never made it's way to the remote cache. - unset( $this->object_cache->cache[ $cache_key ] ); - $this->object_cache->no_mc_groups = []; - self::assertFalse( $this->object_cache->get( 'key', $group ) ); - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_set( $value, $update_value ) { - $cache_key = $this->object_cache->key( 'key', 'default' ); - - // Set to memcached. - self::assertTrue( $this->object_cache->set( 'key', $value ) ); - - // Check local cache. - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $value, - 'found' => true, - ] ); - - // Update with new value. - self::assertTrue( $this->object_cache->set( 'key', $update_value ) ); - - // Check local cache again. - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $update_value, - 'found' => true, - ] ); - - // TODO: Test local cache result after a failed memcached set - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_set_for_non_persistent_groups( $value ) { - $group = 'do-not-persist-me'; - - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Set in local cache. - self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); - - // Check local cache. - $cache_key = $this->object_cache->key( 'key', $group ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $value, - 'found' => false, - ] ); - - // Never made it's way to the remote cache. - unset( $this->object_cache->cache[ $cache_key ] ); - $this->object_cache->no_mc_groups = []; - self::assertFalse( $this->object_cache->get( 'key', $group ) ); - } - - public function test_set_multiple() { - $inputs = $this->data_cache_inputs(); - - $values = []; - $expected = []; - foreach ( $inputs as $key => $input_array ) { - $values[ $key ] = $input_array[0]; - $expected[ $key ] = true; - } - - // Set to memcached. - self::assertEquals( $this->object_cache->set_multiple( $values ), $expected ); - - // Check local cache. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $input_array[0], - 'found' => true, - ] ); - } - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_get( $value ) { - $cache_key = $this->object_cache->key( 'key', 'default' ); - - // Not found intially. - $found = null; - self::assertFalse( $this->object_cache->get( 'key', 'default', false, $found ) ); - self::assertFalse( $found ); - - // Local cache stored the "not found" state. - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => false, - 'found' => false, - ] ); - - // Will return from local cache if present. - $this->object_cache->cache[ $cache_key ] = [ - 'value' => $value, - 'found' => true, - ]; - $found = null; - self::assertEquals( $this->object_cache->get( 'key', 'default', false, $found ), $value ); - self::assertTrue( $found ); - - // But will skip local cache if forced. - $found = null; - self::assertFalse( $this->object_cache->get( 'key', 'default', true, $found ) ); - self::assertFalse( $found ); - - // Actually set the value remotely now. - self::assertTrue( $this->object_cache->set( 'key', $value ) ); - - $found = null; - self::assertEquals( $this->object_cache->get( 'key', 'default', false, $found ), $value ); - self::assertTrue( $found ); - - // Check that the local cache was saved. - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $value, - 'found' => true, - ] ); - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_get_for_non_persistent_groups( $value ) { - $group = 'do-not-persist-me'; - $cache_key = $this->object_cache->key( 'key', $group ); - - // Before we start, let's put a value in the remote cache then remove from local. - self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); - unset( $this->object_cache->cache[ $cache_key ] ); - - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Fetch from local cache. - $found = null; - self::assertFalse( $this->object_cache->get( 'key', $group, true, $found ) ); - self::assertFalse( $found ); - - // Set in local cache and check again. $found is still false by design. - self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); - $found = null; - self::assertEquals( $this->object_cache->get( 'key', $group, true, $found ), $value ); - self::assertFalse( $found ); - } - - public function test_get_multiple() { - $inputs = $this->data_cache_inputs(); - $keys = array_keys( $inputs ); - - $values = []; - $expected_first = []; - $expected_second = []; - foreach ( $inputs as $key => $input_array ) { - $values[ $key ] = $input_array[0]; - $expected_first[ $key ] = false; - $expected_second[ $key ] = $input_array[0]; - } - - // Each is not found intially. - self::assertEquals( $this->object_cache->get_multiple( $keys ), $expected_first ); - - // Will return from local cache if present. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - - $this->object_cache->cache[ $cache_key ] = [ - 'value' => $input_array[0], - 'found' => true, - ]; - } - self::assertEquals( $this->object_cache->get_multiple( $keys ), $expected_second ); - - // But will skip local cache if forced. - self::assertEquals( $this->object_cache->get_multiple( $keys, 'default', true ), $expected_first ); - - // Set values in remote memcached now, but clear out local cache before fetching. - $this->object_cache->set_multiple( $values ); - $this->object_cache->flush_runtime(); - $fetch_keys = array_merge( $keys, [ 'non-existant-key' ] ); - $expected_second['non-existant-key'] = false; - - self::assertEquals( $this->object_cache->get_multiple( $fetch_keys ), $expected_second ); - - // Ensure local cache was saved. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - self::assertEquals( $this->object_cache->cache[ $cache_key ], [ - 'value' => $input_array[0], - 'found' => true, - ] ); - } - - // As well as saved for the non-existant key. - $non_existant_cache_key = $this->object_cache->key( 'non-existant-key', 'default' ); - self::assertEquals( $this->object_cache->cache[ $non_existant_cache_key ], [ - 'value' => false, - 'found' => false, - ] ); - - // Ensure we still get an array if no keys are found. - self::assertEquals( $this->object_cache->get_multiple( [ 'non-existant-key2', 'non-existant-key3' ] ), [ - 'non-existant-key2' => false, - 'non-existant-key3' => false, - ] ); - - // Test super large multiGets that should be broken up into multiple batches. - $large_mget_values = [ - 'mget_1' => '1', - 'mget_500' => '500', - 'mget_999' => '999', - 'mget_1001' => '1001', - 'mget_1500' => '1500', - 'mget_1999' => '1999', - 'mget_2001' => '2001', - 'mget_2500' => '2500', - 'mget_2999' => '2999', - 'mget_3001' => '3001', - 'mget_3500' => '3500', - 'mget_3999' => '3999', - ]; - $this->object_cache->set_multiple( $large_mget_values ); - $this->object_cache->flush_runtime(); - - $expected_large_mget_values = []; - for ( $i = 0; $i < 4000; $i++ ) { - $key = 'mget_' . $i; - $expected_large_mget_values[ $key ] = isset( $large_mget_values[ $key ] ) ? $large_mget_values[ $key ] : false; - } - - self::assertEquals( $this->object_cache->get_multiple( array_keys( $expected_large_mget_values ) ), $expected_large_mget_values ); - } - - public function test_get_multiple_for_non_persistent_groups() { - $inputs = $this->data_cache_inputs(); - $keys = array_keys( $inputs ); - $group = 'do-not-persist-me'; - - $local_values = []; - $remote_values = []; - $expected_first = []; - $expected_second = []; - foreach ( $inputs as $key => $input_array ) { - $local_values[ $key ] = $input_array[0]; - $remote_values[ $key ] = $input_array[1]; - $expected_first[ $key ] = false; - $expected_second[ $key ] = $input_array[0]; - } - - // Before we start, let's put value in the remote cache then remove from local. - $this->object_cache->set_multiple( $remote_values, $group ); - $this->object_cache->flush_runtime(); - - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Fetch from local cache, should be empty. - self::assertEquals( $this->object_cache->get_multiple( $keys, $group ), $expected_first ); - - // Set in local cache and check again. - $this->object_cache->set_multiple( $local_values, $group ); - self::assertEquals( $this->object_cache->get_multiple( $keys, $group ), $expected_second ); - } - - public function test_get_multi() { - $inputs = $this->data_cache_inputs(); - $keys = array_keys( $inputs ); - - $values = []; - $expected = []; - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - - $values[ $key ] = $input_array[0]; - $expected[ $cache_key ] = $input_array[0]; - } - - $non_existant_cache_key = $this->object_cache->key( 'non-existant-key', 'default' ); - $keys = array_merge( $keys, [ 'non-existant-key' ] ); - $expected[ $non_existant_cache_key ] = false; - - // Populate in memcached but flush local cache after. - $this->object_cache->set_multiple( $values ); - $this->object_cache->flush_runtime(); - - self::assertEquals( $this->object_cache->get_multi( [ 'default' => $keys ] ), $expected ); - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_delete( $value ) { - $cache_key = $this->object_cache->key( 'key', 'default' ); - - // Nothing to delete yet. - self::assertFalse( $this->object_cache->delete( 'key' ) ); - - // Now it can delete. - self::assertTrue( $this->object_cache->set( 'key', $value ) ); - self::assertTrue( $this->object_cache->delete( 'key' ) ); - - // Also removed from local cache. - self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); - } - - /** - * @dataProvider data_cache_inputs - */ - public function test_delete_for_non_persistent_groups( $value ) { - $group = 'do-not-persist-me'; - $cache_key = $this->object_cache->key( 'key', $group ); - - // Set in remote cache first, then add to non-persistent groups. - self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); - $this->object_cache->flush_runtime(); - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Nothing to delete yet. - self::assertFalse( $this->object_cache->delete( 'key', $group ) ); - - // Now it can locally delete. - self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); - self::assertTrue( $this->object_cache->delete( 'key', $group ) ); - self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); - - // But never made it's way to actually deleting from the remote cache. - $this->object_cache->no_mc_groups = []; - self::assertEquals( $this->object_cache->get( 'key', $group ), $value ); - } - - public function test_delete_multiple() { - $inputs = $this->data_cache_inputs(); - $keys = array_keys( $inputs ); - - $values = []; - $expected_first = []; - $expected_second = []; - foreach ( $inputs as $key => $input_array ) { - $values[ $key ] = $input_array[0]; - $expected_first[ $key ] = false; - $expected_second[ $key ] = true; - } - - // Nothing to delete yet. - self::assertEquals( $this->object_cache->delete_multiple( $keys ), $expected_first ); - - // Now it can delete. - $keys = array_merge( $keys, [ 'non-existant-key' ] ); - $expected_second['non-existant-key'] = false; - $this->object_cache->set_multiple( $values ); - self::assertEquals( $this->object_cache->delete_multiple( $keys ), $expected_second ); - - // Also removed from local cache. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, 'default' ); - self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); - } - } - - public function test_delete_multiple_for_non_persistent_groups() { - $inputs = $this->data_cache_inputs(); - $keys = array_keys( $inputs ); - $group = 'do-not-persist-me'; - - $values = []; - $expected_first = []; - $expected_second = []; - $expected_third = []; - foreach ( $inputs as $key => $input_array ) { - $values[ $key ] = $input_array[0]; - $expected_first[ $key ] = false; - $expected_second[ $key ] = true; - $expected_third[ $key ] = $input_array[0]; - } - - // Set in remote cache first, then add to non-persistent groups. - $this->object_cache->set_multiple( $values, $group ); - $this->object_cache->flush_runtime(); - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Nothing to delete yet. - self::assertEquals( $this->object_cache->delete_multiple( $keys, $group ), $expected_first ); - - // Now it can delete. - $keys = array_merge( $keys, [ 'non-existant-key' ] ); - $expected_second['non-existant-key'] = false; - $this->object_cache->set_multiple( $values, $group ); - self::assertEquals( $this->object_cache->delete_multiple( $keys, $group ), $expected_second ); - - // Also removed from local cache. - foreach ( $inputs as $key => $input_array ) { - $cache_key = $this->object_cache->key( $key, $group ); - self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); - } - - // But not from remote cache. - $this->object_cache->no_mc_groups = []; - self::assertEquals( $this->object_cache->get_multiple( array_keys( $inputs ), $group ), $expected_third ); - } - - public function test_incr() { - // Increments by 1 by default. - $this->object_cache->add( 'key', 1 ); - self::assertEquals( $this->object_cache->incr( 'key' ), 2 ); - - // Can increment by a specified amount - $this->object_cache->add( 'key2', 1 ); - self::assertEquals( $this->object_cache->incr( 'key2', 5 ), 6 ); - - // Returns false if key doesn't exist yet. - self::assertFalse( $this->object_cache->incr( 'key3' ) ); - - // Memcache extension throws notices for the following tests, memcached does not (despite what the docs say). - if ( ! $this->is_using_memcached_ext ) { - self::expectNotice(); - } - - // Fails if value is non-int. - $this->object_cache->add( 'key4', 'non-numeric' ); - self::assertFalse( $this->object_cache->incr( 'key4' ) ); - - $this->object_cache->add( 'key5', [ 'non-numeric' ] ); - self::assertFalse( $this->object_cache->incr( 'key5' ) ); - - $this->object_cache->add( 'key6', 1.234 ); - self::assertFalse( $this->object_cache->incr( 'key6' ) ); - } - - public function test_incr_for_non_persistent_groups() { - $group = 'do-not-persist-me'; - $cache_key = $this->object_cache->key( 'key', $group ); - - // Set in remote cache first, then add to non-persistent groups. - self::assertTrue( $this->object_cache->set( 'key', 100, $group ) ); - $this->object_cache->flush_runtime(); - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Nothing to increment yet. - self::assertFalse( $this->object_cache->incr( 'key', 1, $group ) ); - - // Now it can locally increment. - self::assertTrue( $this->object_cache->add( 'key', 0, $group ) ); - self::assertEquals( $this->object_cache->incr( 'key', 2, $group ), 2 ); - - // Fails if value is non-int. - $this->object_cache->add( 'key2', 'non-numeric', $group ); - self::assertFalse( $this->object_cache->incr( 'key2', 1, $group ) ); - - $this->object_cache->add( 'key3', [ 'non-numeric' ], $group ); - self::assertFalse( $this->object_cache->incr( 'key3', 1, $group ) ); - - $this->object_cache->add( 'key4', 1.234, $group ); - self::assertFalse( $this->object_cache->incr( 'key4', 1, $group ) ); - - // But the changes never made their way to the remote cache. - $this->object_cache->flush_runtime(); - $this->object_cache->no_mc_groups = []; - self::assertEquals( $this->object_cache->get( 'key', $group ), 100 ); - } - - public function test_decr() { - // Decrement by 1 by default. - $this->object_cache->add( 'key', 1 ); - self::assertEquals( $this->object_cache->decr( 'key' ), 0 ); - - // Can decrement by a specified amount - $this->object_cache->add( 'key2', 5 ); - self::assertEquals( $this->object_cache->decr( 'key2', 2 ), 3 ); - - // Returns false if key doesn't exist yet. - self::assertFalse( $this->object_cache->decr( 'key3' ) ); - - // Returns zero if decremented value would have been less than 0. - $this->object_cache->add( 'key4', 2 ); - self::assertEquals( $this->object_cache->decr( 'key4', 3 ), 0 ); - - // Memcache extension throws notices for the following tests, memcached does not (despite what the docs say). - if ( ! $this->is_using_memcached_ext ) { - self::expectNotice(); - } - - // Fails if value is non-int. - $this->object_cache->add( 'key5', 'non-numeric' ); - self::assertFalse( $this->object_cache->decr( 'key5' ) ); - - $this->object_cache->add( 'key6', [ 'non-numeric' ] ); - self::assertFalse( $this->object_cache->decr( 'key6' ) ); - - $this->object_cache->add( 'key7', 1.234 ); - self::assertFalse( $this->object_cache->decr( 'key7' ) ); - } - - public function test_decr_for_non_persistent_groups() { - $group = 'do-not-persist-me'; - $cache_key = $this->object_cache->key( 'key', $group ); - - // Set in remote cache first, then add to non-persistent groups. - self::assertTrue( $this->object_cache->set( 'key', 100, $group ) ); - $this->object_cache->flush_runtime(); - $this->object_cache->add_non_persistent_groups( [ $group ] ); - - // Nothing to decrement yet. - self::assertFalse( $this->object_cache->decr( 'key', 1, $group ) ); - - // Now it can locally decrement. - self::assertTrue( $this->object_cache->add( 'key', 4, $group ) ); - self::assertEquals( $this->object_cache->decr( 'key', 2, $group ), 2 ); - - // Returns zero if decremented value would have been less than 0. - $this->object_cache->add( 'key2', 2 ); - self::assertEquals( $this->object_cache->decr( 'key2', 3 ), 0 ); - - // Fails if value is non-int. - $this->object_cache->add( 'key3', 'non-numeric', $group ); - self::assertFalse( $this->object_cache->decr( 'key3', 1, $group ) ); - - $this->object_cache->add( 'key4', [ 'non-numeric' ], $group ); - self::assertFalse( $this->object_cache->decr( 'key4', 1, $group ) ); - - $this->object_cache->add( 'key5', 1.234, $group ); - self::assertFalse( $this->object_cache->decr( 'key5', 1, $group ) ); - - // But the changes never made their way to the remote cache. - $this->object_cache->flush_runtime(); - $this->object_cache->no_mc_groups = []; - self::assertEquals( $this->object_cache->get( 'key', $group ), 100 ); - } - - public function test_flush() { - // Add a value to memcached (local and remote) - self::assertTrue( $this->object_cache->add( 'key', 'data' ) ); - $cache_key = $this->object_cache->key( 'key', 'default' ); - self::assertNotEmpty( $this->object_cache->cache[ $cache_key ] ); - - $initial_site_flush_number = $this->object_cache->flush_number[ $this->object_cache->blog_prefix ]; - $initial_global_flush_number = $this->object_cache->global_flush_number; - self::assertTrue( $this->object_cache->flush() ); - $new_site_flush_number = $this->object_cache->flush_number[ $this->object_cache->blog_prefix ]; - $new_global_flush_number = $this->object_cache->global_flush_number; - - // Ensure the local cache was flushed. - // Note: Local cache won't be completely empty because it stores updated flush number in local cache. - self::assertArrayNotHasKey( $cache_key, $this->object_cache->cache ); - - // Ensure it can't pull from remote cache either. - self::assertFalse( $this->object_cache->get( 'key' ) ); - - // Ensure the flush keys were rotated. - self::assertNotEmpty( $new_site_flush_number ); - self::assertNotEmpty( $new_global_flush_number ); - self::assertNotEquals( $initial_site_flush_number, $new_site_flush_number ); - self::assertNotEquals( $initial_global_flush_number, $new_global_flush_number ); - } - - public function test_flush_runtime() { - // Add a value to memcached (local and remote) - self::assertTrue( $this->object_cache->add( 'key', 'data' ) ); - $cache_key = $this->object_cache->key( 'key', 'default' ); - self::assertNotEmpty( $this->object_cache->cache[ $cache_key ] ); - self::assertNotEmpty( $this->object_cache->group_ops ); - - self::assertTrue( $this->object_cache->flush_runtime() ); - - // Gone from local cache. - self::assertArrayNotHasKey( $cache_key, $this->object_cache->cache ); - self::assertEquals( $this->object_cache->group_ops, [] ); - - // But exists remotely still - self::assertEquals( $this->object_cache->get( 'key' ), 'data' ); - } - - public function test_add_global_groups() { - // Starts out with one element in array already. - self::assertEquals( $this->object_cache->global_groups, [ $this->object_cache->global_flush_group ] ); - - // Accepts a single string. - $single_group = 'single-group'; - $this->object_cache->add_global_groups( $single_group ); - $this->assertContains( $single_group, $this->object_cache->global_groups ); - - // Or an array (but removes duplicates). - $duplicate_groups = [ $single_group, 'another-group', 'another-group' ]; - $this->object_cache->add_global_groups( $duplicate_groups ); - $this->assertContains( $single_group, $this->object_cache->global_groups ); - $this->assertContains( 'another-group', $this->object_cache->global_groups ); - $this->assertEquals( count( $this->object_cache->global_groups ), 3 ); - } - - public function test_add_non_persistent_groups() { - // Starts out as an empty array. - self::assertEmpty( $this->object_cache->no_mc_groups ); - - // Accepts a single string. - $single_group = 'single-group'; - $this->object_cache->add_non_persistent_groups( $single_group ); - $this->assertContains( $single_group, $this->object_cache->no_mc_groups ); - - // Or an array (but removes duplicates). - $duplicate_groups = [ $single_group, 'another-group', 'another-group' ]; - $this->object_cache->add_non_persistent_groups( $duplicate_groups ); - $this->assertContains( $single_group, $this->object_cache->no_mc_groups ); - $this->assertContains( 'another-group', $this->object_cache->no_mc_groups ); - $this->assertEquals( count( $this->object_cache->no_mc_groups ), 2 ); - } - - public function test_switch_to_blog() { - global $table_prefix; - - $initial_blog_prefix = $this->object_cache->blog_prefix; - $this->object_cache->switch_to_blog( 2 ); - - if ( is_multisite() ) { - self::assertEquals( $this->object_cache->blog_prefix, 2 ); - } else { - self::assertEquals( $this->object_cache->blog_prefix, $table_prefix ); - } - } - - public function test_close() { - self::assertTrue( $this->object_cache->close() ); - - // TODO: Further testing requires being able to be able to inject/mock the adapters. - } - - /* - |-------------------------------------------------------------------------- - | Various edge cases. - |-------------------------------------------------------------------------- - */ - - public function test_large_key_lengths() { - $large_keys = [ - str_repeat( 'a', 100 ) => 'a value', - str_repeat( 'b', 250 ) => 'b value', - str_repeat( 'c', 1000 ) => 'c value', - ]; - - foreach ( $large_keys as $key => $_value ) { - self::assertTrue( $this->object_cache->add( $key, 1 ) ); - self::assertTrue( $this->object_cache->replace( $key, 2 ) ); - self::assertTrue( $this->object_cache->set( $key, 3 ) ); - self::assertEquals( $this->object_cache->incr( $key ), 4 ); - self::assertEquals( $this->object_cache->decr( $key, 3 ), 1 ); - self::assertEquals( $this->object_cache->get( $key ), 1 ); - self::assertTrue( $this->object_cache->delete( $key ) ); - } - - self::assertSame( $this->object_cache->add_multiple( $large_keys ), array_map( fn() => true, $large_keys ) ); - self::assertSame( $this->object_cache->delete_multiple( array_keys( $large_keys ) ), array_map( fn() => true, $large_keys ) ); - self::assertSame( $this->object_cache->set_multiple( $large_keys ), array_map( fn() => true, $large_keys ) ); - self::assertSame( $this->object_cache->get_multiple( array_keys( $large_keys ) ), $large_keys ); - } - - /* - |-------------------------------------------------------------------------- - | Internal methods, mostly deals with flush numbers, the pseudo-cache-flushing mechanic. - |-------------------------------------------------------------------------- - */ - - public function test_flush_prefix() { - // Does not flush the flush groups. - self::assertEquals( '_:', $this->object_cache->flush_prefix( $this->object_cache->flush_group ) ); - self::assertEquals( '_:', $this->object_cache->flush_prefix( $this->object_cache->global_flush_group ) ); - - // TODO: sets global vs sets blog prefix. - // test_flush_prefix_sets_global_flush_number_for_global_groups - // test_flush_prefix_sets_flush_number_for_non_global_groups - } - - public function test_key() { - // Uses "default" group by default. - self::assertStringContainsString( 'default:foo', $this->object_cache->key( 'foo', '' ) ); - - // Contains global prefix for global groups. - $this->object_cache->add_global_groups( [ 'global-group' ] ); - $this->object_cache->global_prefix = 'global_prefix'; // Mock for non-multisite tests. - self::assertStringContainsString( $this->object_cache->global_prefix, $this->object_cache->key( 'foo', 'global-group' ) ); - - // Contains blog prefix for non-global groups. - $this->object_cache->blog_prefix = 'blog_prefix'; // Mock for non-multisite tests. - $this->assertStringContainsString( $this->object_cache->blog_prefix, $this->object_cache->key( 'foo', 'non-global-group' ) ); - } - - /* - |-------------------------------------------------------------------------- - | Testing Utils - |-------------------------------------------------------------------------- - */ - - public function data_cache_inputs() { - $object = new stdClass(); - $object->property = 'test'; - - // Key => [ first value, updated value ] - return [ - 'empty-string' => [ '', 'updated' ], - 'empty-array' => [ [], [ 'updated' ] ], - 'empty-object' => [ new stdClass(), $object ], - 'zero' => [ 0, 1 ], - 'one' => [ 1, 0 ], - 'false' => [ false, true ], - 'true' => [ true, false ], - 'null' => [ null, 'notnull' ], - 'basic-array' => [ [ 'basic', 'array' ], [] ], - 'complex-array' => [ - [ - 'a' => 'multi', - 'dimensional' => [ 'array', 'example' ], - ], - [], - ], - 'string' => [ 'string', '' ], - 'float' => [ 1.234, 5.678 ], - 'object' => [ $object, new stdClass() ], - ]; - } -} From f15795d3d705b2b0136cc94a119f0a2e588e32ed Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Thu, 21 Nov 2024 16:38:48 -0600 Subject: [PATCH 2/4] Squashed 'drop-ins/wp-memcached/' content from commit 43e603a1f git-subtree-dir: drop-ins/wp-memcached git-subtree-split: 43e603a1f5f17f3863ddececb044a3c93478d579 --- .gitignore | 2 + bin/test.sh | 148 ++ composer.json | 38 + composer.lock | 4418 ++++++++++++++++++++++++++++++++ includes/adapter-interface.php | 160 ++ includes/memcache-adapter.php | 389 +++ includes/memcached-adapter.php | 492 ++++ includes/stats.php | 381 +++ includes/wp-object-cache.php | 1128 ++++++++ object-cache.php | 398 +++ phpcs.xml.dist | 29 + phpunit.xml.dist | 25 + psalm.xml.dist | 23 + readme.md | 39 + stubs/memcache-stubs.php | 444 ++++ stubs/memcached-stubs.php | 1528 +++++++++++ stubs/wordpress-stubs.php | 130 + tests/bootstrap.php | 34 + tests/test-wp-object-cache.php | 1059 ++++++++ 19 files changed, 10865 insertions(+) create mode 100644 .gitignore create mode 100755 bin/test.sh create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 includes/adapter-interface.php create mode 100644 includes/memcache-adapter.php create mode 100644 includes/memcached-adapter.php create mode 100644 includes/stats.php create mode 100644 includes/wp-object-cache.php create mode 100644 object-cache.php create mode 100644 phpcs.xml.dist create mode 100644 phpunit.xml.dist create mode 100644 psalm.xml.dist create mode 100644 readme.md create mode 100644 stubs/memcache-stubs.php create mode 100644 stubs/memcached-stubs.php create mode 100644 stubs/wordpress-stubs.php create mode 100644 tests/bootstrap.php create mode 100644 tests/test-wp-object-cache.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..cae6dff879 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +/coverage/ diff --git a/bin/test.sh b/bin/test.sh new file mode 100755 index 0000000000..f08bdee81e --- /dev/null +++ b/bin/test.sh @@ -0,0 +1,148 @@ +#!/bin/sh + +while [ $# -gt 0 ]; do + case "$1" in + --wp) + shift + WP_VERSION="$1" + ;; + + --multisite) + shift + WP_MULTISITE="$1" + ;; + + --php) + shift + PHP_VERSION="$1" + ;; + + --php-options) + shift + PHP_OPTIONS="$1" + ;; + + --phpunit) + shift + PHPUNIT_VERSION="$1" + ;; + + --network) + shift + NETWORK_NAME_OVERRIDE="$1" + ;; + + --dbhost) + shift + MYSQL_HOST_OVERRIDE="$1" + ;; + + --docker-options) + shift + DOCKER_OPTIONS="$1" + ;; + + *) + ARGS="${ARGS} $1" + ;; + esac + + shift +done + +: "${WP_VERSION:=latest}" +: "${WP_MULTISITE:=0}" +: "${PHP_VERSION:=""}" +: "${PHP_OPTIONS:=""}" +: "${PHPUNIT_VERSION:=""}" +: "${DOCKER_OPTIONS:=""}" + +PHP_OPTIONS="-d apc.enable_cli=1 ${PHP_OPTIONS}" + +export WP_VERSION +export WP_MULTISITE +export PHP_VERSION +export PHP_OPTIONS +export PHPUNIT_VERSION + +echo "--------------" +echo "Will test with WP_VERSION=${WP_VERSION} and WP_MULTISITE=${WP_MULTISITE}" +echo "--------------" +echo + +MARIADB_VERSION="10.3" + +UUID=$(date +%s000) +if [ -z "${NETWORK_NAME_OVERRIDE}" ]; then + NETWORK_NAME="tests-${UUID}" + docker network create "${NETWORK_NAME}" +else + NETWORK_NAME="${NETWORK_NAME_OVERRIDE}" +fi + +export MYSQL_USER=wordpress +export MYSQL_PASSWORD=wordpress +export MYSQL_DATABASE=wordpress_test + +db="" +if [ -z "${MYSQL_HOST_OVERRIDE}" ]; then + MYSQL_HOST="db-${UUID}" + db=$(docker run --rm --network "${NETWORK_NAME}" --name "${MYSQL_HOST}" -e MYSQL_ROOT_PASSWORD="wordpress" -e MARIADB_INITDB_SKIP_TZINFO=1 -e MYSQL_USER -e MYSQL_PASSWORD -e MYSQL_DATABASE -d "mariadb:${MARIADB_VERSION}") +else + MYSQL_HOST="${MYSQL_HOST_OVERRIDE}" +fi + +export MYSQL_HOST + +export MEMCACHED_HOST_1="mc1-${UUID}" +export MEMCACHED_HOST_2="mc2-${UUID}" +mc1=$(docker run --rm --network "${NETWORK_NAME}" --name "${MEMCACHED_HOST_1}" -d memcached:latest) +mc2=$(docker run --rm --network "${NETWORK_NAME}" --name "${MEMCACHED_HOST_2}" -d memcached:latest) + +cleanup() { + if [ -n "${db}" ]; then + docker rm -f "${db}" + fi + + if [ -n "${mc1}" ]; then + docker rm -f "${mc1}" + fi + + if [ -n "${mc2}" ]; then + docker rm -f "${mc2}" + fi + + if [ -z "${NETWORK_NAME_OVERRIDE}" ]; then + docker network rm "${NETWORK_NAME}" + fi +} + +trap cleanup EXIT + +if [ -z "${CI}" ]; then + interactive="-it" +else + interactive="" +fi + +# shellcheck disable=SC2086,SC2248,SC2312 # ARGS and DOCKER_OPTIONS must not be quoted +docker run \ + ${interactive} \ + --rm \ + --network "${NETWORK_NAME}" \ + -e WP_VERSION \ + -e WP_MULTISITE \ + -e PHP_VERSION \ + -e PHP_OPTIONS \ + -e PHPUNIT_VERSION \ + -e MYSQL_USER \ + -e MYSQL_PASSWORD \ + -e MYSQL_DB="${MYSQL_DATABASE}" \ + -e MYSQL_HOST \ + -e XDEBUG_MODE=coverage \ + -e MEMCACHED_HOST_1="${MEMCACHED_HOST_1}:11211" \ + -e MEMCACHED_HOST_2="${MEMCACHED_HOST_2}:11211" \ + ${DOCKER_OPTIONS} \ + -v "$(pwd):/home/circleci/project" \ + ghcr.io/automattic/vip-container-images/wp-test-runner:latest \ + ${ARGS} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000..8e6e0a6998 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "automattic/wp-cache-memcached", + "description": "Memcached Object Cache for WordPress", + "type": "wordpress-dropin", + "require": { + "php": ">= 7.4" + }, + "require-dev": { + "automattic/vipwpcs": "^2.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.0", + "wp-phpunit/wp-phpunit": "^6.1", + "yoast/phpunit-polyfills": "^1.1" + }, + "license": "MIT", + "authors": [ + { + "name": "Volodymyr Kolesnykov", + "email": "volodymyr@wildwolf.name" + } + ], + "config": { + "sort-packages": true, + "platform": { + "php": "7.4" + }, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "scripts": { + "phpcs": "phpcs", + "phpcs:fix": "phpcbf", + "psalm": "psalm" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..6ce8304759 --- /dev/null +++ b/composer.lock @@ -0,0 +1,4418 @@ +{ + "_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": "89c422a14c317e64af29d024db06cd13", + "packages": [], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "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", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "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.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "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", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "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": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "automattic/vipwpcs", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/Automattic/VIP-Coding-Standards.git", + "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", + "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php": ">=5.4", + "sirbrillig/phpcs-variable-analysis": "^2.11.1", + "squizlabs/php_codesniffer": "^3.5.5", + "wp-coding-standards/wpcs": "^2.3" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^0.5", + "php-parallel-lint/php-parallel-lint": "^1.0", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" + }, + "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", + "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": "2021-09-29T16:20:23+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.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": "2022-01-17T14:14:24+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "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.1.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": "2022-11-17T09:50:14+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "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": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/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": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "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", + "symfony/phpunit-bridge": "^6.0" + }, + "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": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.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": "2022-02-25T21:32:43+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.2", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\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/Dealerdirect/phpcodesniffer-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/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2022-02-04T12:51:07+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/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": "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.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "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.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "0.4.1", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", + "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "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": "^9.5.26 || ^8.5.31", + "theofidry/php-cs-fixer-config": "^1.0", + "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/0.4.1" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-16T22:01:02+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "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", + "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.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.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.1.0" + }, + "time": "2022-12-08T20:46:14+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.3", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^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.15.3" + }, + "time": "2023-01-16T22:05:37+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "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.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", + "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || 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", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "time": "2022-10-25T01:46:02+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", + "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || 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", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "time": "2022-10-24T09:00:36+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.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "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": "account@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.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "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.6.2" + }, + "time": "2022-10-14T12:47:21+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.24", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.14", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library 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", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-01-26T08:26:55+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.5.28", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-01-14T12:32:24+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "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.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+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.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "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.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "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.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "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.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-03T09:37:03+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "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.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "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.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "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.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+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.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "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": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "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" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "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.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-12T14:47: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": "sirbrillig/phpcs-variable-analysis", + "version": "v2.11.10", + "source": { + "type": "git", + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "0f25a3766f26df91d6bdda0c8931303fc85499d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/0f25a3766f26df91d6bdda0c8931303fc85499d7", + "reference": "0f25a3766f26df91d6bdda0c8931303fc85499d7", + "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", + "sirbrillig/phpcs-import-detection": "^1.1", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" + }, + "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": "2023-01-05T18:45:16+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "2.17.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^7.4|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "phpunit/phpunit": "^9.0", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-26T08:22:07+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.1", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "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": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2022-06-18T07:21:10+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", + "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", + "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.19" + }, + "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": "2023-01-01T08:32:19+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.2" + }, + "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": "2022-01-02T09:53:40+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8", + "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "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.19" + }, + "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": "2023-01-14T19:14:44+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "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": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.2" + }, + "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": "2022-05-30T19:17:29+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb", + "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb", + "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.19" + }, + "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": "2023-01-01T08:32:19+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "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.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "vimeo/psalm", + "version": "5.6.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/e784128902dfe01d489c4123d69918a9f3c1eac5", + "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer/package-versions-deprecated": "^1.10.0", + "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.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.13", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "sebastian/diff": "^4.0 || ^5.0", + "spatie/array-to-xml": "^2.17.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.0", + "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.5", + "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" + }, + "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": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.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" + ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/5.6.0" + }, + "time": "2023-01-23T20:32:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "7da1894633f168fe244afc6de00d141f27517b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", + "reference": "7da1894633f168fe244afc6de00d141f27517b62", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.3.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." + }, + "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", + "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" + }, + "time": "2020-05-13T23:57:56+00:00" + }, + { + "name": "wp-phpunit/wp-phpunit", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/wp-phpunit/wp-phpunit.git", + "reference": "49521597fa525f762a50a4a6d22ed180839519fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/49521597fa525f762a50a4a6d22ed180839519fd", + "reference": "49521597fa525f762a50a4a6d22ed180839519fd", + "shasum": "" + }, + "type": "library", + "autoload": { + "files": [ + "__loaded.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Evan Mattson", + "email": "me@aaemnnost.tv" + }, + { + "name": "WordPress Community", + "homepage": "https://wordpress.org/about/" + } + ], + "description": "WordPress core PHPUnit library", + "homepage": "https://github.com/wp-phpunit", + "keywords": [ + "phpunit", + "test", + "wordpress" + ], + "support": { + "docs": "https://github.com/wp-phpunit/docs", + "issues": "https://github.com/wp-phpunit/issues", + "source": "https://github.com/wp-phpunit/wp-phpunit" + }, + "time": "2022-11-02T12:52:44+00:00" + }, + { + "name": "yoast/phpunit-polyfills", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", + "reference": "e9c8413de4c8ae03d2923a44f17d0d7dad1b96be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/e9c8413de4c8ae03d2923a44f17d0d7dad1b96be", + "reference": "e9c8413de4c8ae03d2923a44f17d0d7dad1b96be", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "files": [ + "phpunitpolyfills-autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Team Yoast", + "email": "support@yoast.com", + "homepage": "https://yoast.com" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" + } + ], + "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", + "keywords": [ + "phpunit", + "polyfill", + "testing" + ], + "support": { + "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", + "source": "https://github.com/Yoast/PHPUnit-Polyfills" + }, + "time": "2024-09-06T22:03:10+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">= 7.4" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.4" + }, + "plugin-api-version": "2.6.0" +} diff --git a/includes/adapter-interface.php b/includes/adapter-interface.php new file mode 100644 index 0000000000..eec8983108 --- /dev/null +++ b/includes/adapter-interface.php @@ -0,0 +1,160 @@ +>|array $memcached_servers + * + * @return void + */ + public function __construct( array $memcached_servers ); + + /** + * Get a list of all connection pools, indexed by group name. + * + * @psalm-return array + */ + public function get_connections(); + + /** + * Get a connection for each individual default server. + * + * @psalm-return array + */ + public function get_default_connections(); + + /** + * Get list of servers with connection errors. + * + * @psalm-return array + */ + public function get_connection_errors(); + + /** + * Close the memcached connections. + * + * @return bool + */ + public function close_connections(); + + /** + * Add an item under a new key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function add( $key, $connection_group, $data, $expiration ); + + /** + * Replace the item under an existing key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function replace( $key, $connection_group, $data, $expiration ); + + /** + * Store an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function set( $key, $connection_group, $data, $expiration ); + + /** + * Retrieve an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array{value: mixed, found: bool} + */ + public function get( $key, $connection_group ); + + /** + * Retrieve multiple items. + * + * @param array $keys List of keys to retrieve. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array|false + */ + public function get_multiple( $keys, $connection_group ); + + /** + * Delete an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return boolean + */ + public function delete( $key, $connection_group ); + + /** + * Delete multiple items. + * + * @param string[] $keys Array of the full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array + */ + public function delete_multiple( $keys, $connection_group ); + + /** + * Increment numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to increment the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function increment( $key, $connection_group, $offset ); + + /** + * Decrement numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to decrement the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function decrement( $key, $connection_group, $offset ); + + /** + * Set a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". + * + * @return void + */ + public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ); + + /** + * Get a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * + * @psalm-return array Key is the server's "host:port", value is returned from Memcached. + */ + public function get_with_redundancy( $key ); +} diff --git a/includes/memcache-adapter.php b/includes/memcache-adapter.php new file mode 100644 index 0000000000..8ce08a1b53 --- /dev/null +++ b/includes/memcache-adapter.php @@ -0,0 +1,389 @@ + */ + private array $connections = []; + + /** @psalm-var array */ + private array $default_connections = []; + + /** @psalm-var array */ + private array $connection_errors = []; + + /** + * @psalm-param array>|array $memcached_servers + * + * @return void + */ + public function __construct( array $memcached_servers ) { + if ( is_int( key( $memcached_servers ) ) ) { + $memcached_servers = [ 'default' => $memcached_servers ]; + } + + /** @psalm-var array> $memcached_servers */ + foreach ( $memcached_servers as $bucket => $addresses ) { + $this->connections[ $bucket ] = new \Memcache(); + + foreach ( $addresses as $address ) { + $parsed_address = $this->parse_address( $address ); + $config = $this->get_config_options(); + + $this->connections[ $bucket ]->addServer( + $parsed_address['host'], + $parsed_address['port'], + $config['persistent'], + $config['weight'], + $config['timeout'], + $config['retry_interval'], + $config['status'], + $config['failure_callback'], + ); + + $this->connections[ $bucket ]->setCompressThreshold( $config['compress_threshold'], $config['min_compress_savings'] ); + + // Prepare individual connections to servers in the default bucket for flush_number redundancy. + if ( 'default' === $bucket ) { + $memcache = new \Memcache(); + + $memcache->addServer( + $parsed_address['host'], + $parsed_address['port'], + $config['persistent'], + $config['weight'], + $config['timeout'], + $config['retry_interval'], + $config['status'], + $config['failure_callback'], + ); + + $this->default_connections[ $parsed_address['host'] . ':' . $parsed_address['port'] ] = $memcache; + } + } + } + } + + /* + |-------------------------------------------------------------------------- + | Connection-related adapter methods. + |-------------------------------------------------------------------------- + */ + + /** + * Get a list of all connection pools, indexed by group name. + * + * @psalm-return array + */ + public function get_connections() { + return $this->connections; + } + + /** + * Get a connection for each individual default server. + * + * @psalm-return array + */ + public function get_default_connections() { + return array_values( $this->default_connections ); + } + + /** + * Get list of servers with connection errors. + * + * @psalm-return array + */ + public function &get_connection_errors() { + return $this->connection_errors; + } + + /** + * Close the memcached connections. + * Note that Memcache::close() doesn't close persistent connections, but does free up some memory. + * + * @return bool + */ + public function close_connections() { + // TODO: Should probably "close" $default_connections too? + foreach ( $this->connections as $connection ) { + $connection->close(); + } + + return true; + } + + /* + |-------------------------------------------------------------------------- + | Main adapter methods for memcached server interactions. + |-------------------------------------------------------------------------- + */ + + /** + * Add an item under a new key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function add( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ + return $mc->add( $key, $data, false, $expiration ); + } + + /** + * Replace the item under an existing key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function replace( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ + return $mc->replace( $key, $data, false, $expiration ); + } + + /** + * Store an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function set( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + /** @psalm-suppress InvalidArgument -- the 3rd arg can be false */ + return $mc->set( $key, $data, false, $expiration ); + } + + /** + * Retrieve an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array{value: mixed, found: bool} + */ + public function get( $key, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + + $flags = false; + /** @psalm-suppress InvalidArgument $flags can be false */ + $value = $mc->get( $key, $flags ); + + /** @psalm-suppress RedundantCondition -- $flags is passed by reference so it may change*/ + return [ + 'value' => $value, + 'found' => false !== $flags, + ]; + } + + /** + * Retrieve multiple items. + * + * @param array $keys List of keys to retrieve. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array|false + */ + public function get_multiple( $keys, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + + $return = []; + foreach ( $keys as $key ) { + $flags = false; + /** @psalm-suppress InvalidArgument $flags can be false */ + $value = $mc->get( $key, $flags ); + + /** @psalm-suppress RedundantCondition -- $flags is passed by reference so it may change*/ + if ( false !== $flags ) { + // Only return if we found the value, similar to Memcached::getMulti() + $return[ $key ] = $value; + } + } + + return $return; + } + + /** + * Delete an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return boolean + */ + public function delete( $key, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + return $mc->delete( $key ); + } + + /** + * Delete multiple items. + * + * @param string[] $keys Array of the full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array + */ + public function delete_multiple( $keys, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + + $return = []; + foreach ( $keys as $key ) { + $return[ $key ] = $mc->delete( $key ); + } + + return $return; + } + + /** + * Increment numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to increment the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function increment( $key, $connection_group, $offset ) { + $mc = $this->get_connection( $connection_group ); + return $mc->increment( $key, $offset ); + } + + /** + * Decrement numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to decrement the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function decrement( $key, $connection_group, $offset ) { + $mc = $this->get_connection( $connection_group ); + return $mc->decrement( $key, $offset ); + } + + /** + * Set a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". + * + * @return void + */ + public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ) { + foreach ( $this->default_connections as $server_string => $mc ) { + if ( is_null( $servers_to_update ) || in_array( $server_string, $servers_to_update, true ) ) { + $mc->set( $key, $data, $expiration ); + } + } + } + + /** + * Get a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * + * @psalm-return array Key is the server's "host:port", value is returned from Memcached. + */ + public function get_with_redundancy( $key ) { + $values = []; + + foreach ( $this->default_connections as $server_string => $mc ) { + /** @psalm-suppress MixedAssignment */ + $values[ $server_string ] = $mc->get( $key ); + } + + return $values; + } + + /* + |-------------------------------------------------------------------------- + | Utils. + |-------------------------------------------------------------------------- + */ + + /** + * @param int|string $group + * @return \Memcache + */ + private function get_connection( $group ) { + return $this->connections[ (string) $group ] ?? $this->connections['default']; + } + + /** + * @param string $address + * @psalm-return array{host: string, port: int} + */ + private function parse_address( string $address ): array { + $default_port = ini_get( 'memcache.default_port' ) ? ini_get( 'memcache.default_port' ) : '11211'; + + if ( 'unix://' == substr( $address, 0, 7 ) ) { + $host = $address; + $port = 0; + } else { + $items = explode( ':', $address, 2 ); + $host = $items[0]; + $port = isset( $items[1] ) ? intval( $items[1] ) : intval( $default_port ); + } + + return [ + 'host' => $host, + 'port' => $port, + ]; + } + + /** + * @param string $host + * @param string $port + */ + public function failure_callback( $host, $port ): void { + $this->connection_errors[] = [ + 'host' => $host, + 'port' => $port, + ]; + } + + /** + * @psalm-return array{ + * persistent: bool, + * weight: int, + * timeout: int, + * retry_interval: int, + * status: bool, + * compress_threshold: int, + * min_compress_savings:float, + * failure_callback: callable, + * } + */ + private function get_config_options(): array { + return [ + 'persistent' => true, + 'weight' => 1, + 'timeout' => 1, + 'retry_interval' => 15, + 'status' => true, + 'compress_threshold' => 20000, + 'min_compress_savings' => 0.2, + 'failure_callback' => [ $this, 'failure_callback' ], + ]; + } +} diff --git a/includes/memcached-adapter.php b/includes/memcached-adapter.php new file mode 100644 index 0000000000..292adbb102 --- /dev/null +++ b/includes/memcached-adapter.php @@ -0,0 +1,492 @@ + */ + private array $connections = []; + + /** @psalm-var array */ + private array $default_connections = []; + + /** @psalm-var array */ + private array $redundancy_server_keys = []; + + /** @psalm-var array */ + private array $connection_errors = []; + + /** + * @psalm-param array>|array $memcached_servers + * + * @return void + */ + public function __construct( array $memcached_servers ) { + if ( is_int( key( $memcached_servers ) ) ) { + $memcached_servers = [ 'default' => $memcached_servers ]; + } + + /** @psalm-var array> $memcached_servers */ + foreach ( $memcached_servers as $bucket => $addresses ) { + $bucket_servers = []; + + foreach ( $addresses as $index => $address ) { + $parsed_address = $this->parse_address( $address ); + $server = [ + 'host' => $parsed_address['host'], + 'port' => $parsed_address['port'], + 'weight' => 1, + ]; + + $bucket_servers[] = $server; + + // Prepare individual connections to servers in the default bucket for flush_number redundancy. + if ( 'default' === $bucket ) { + // Deprecated in this adapter. As long as no requests are made from these pools, the connections should never be established. + $this->default_connections[] = $this->create_connection_pool( 'redundancy-' . $index, [ $server ] ); + } + } + + $this->connections[ $bucket ] = $this->create_connection_pool( 'bucket-' . $bucket, $bucket_servers ); + } + + $this->redundancy_server_keys = $this->get_server_keys_for_redundancy(); + } + + /* + |-------------------------------------------------------------------------- + | Connection-related adapter methods. + |-------------------------------------------------------------------------- + */ + + /** + * Get a list of all connection pools, indexed by group name. + * + * @psalm-return array + */ + public function get_connections() { + return $this->connections; + } + + /** + * Get a connection for each individual default server. + * + * @psalm-return array + */ + public function get_default_connections() { + return $this->default_connections; + } + + /** + * Get list of servers with connection errors. + * + * @psalm-return array + */ + public function &get_connection_errors() { + // Not supported atm. We could look at Memcached::getResultCode() after each call, + // looking for MEMCACHED_CONNECTION_FAILURE for example. + // But it wouldn't tell us the exact host/port that failed. + // Memcached::getStats() is another possibility, though not the same behavior-wise. + return $this->connection_errors; + } + + /** + * Close the memcached connections. + * @return bool + */ + public function close_connections() { + // Memcached::quit() closes persistent connections, which we don't want to do. + return true; + } + + /* + |-------------------------------------------------------------------------- + | Main adapter methods for memcached server interactions. + |-------------------------------------------------------------------------- + */ + + /** + * Add an item under a new key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function add( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + return $mc->add( $this->normalize_key( $key ), $data, $expiration ); + } + + /** + * Replace the item under an existing key. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function replace( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + return $mc->replace( $this->normalize_key( $key ), $data, $expiration ); + } + + /** + * Store an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * + * @return boolean True on success, false on failure. + */ + public function set( $key, $connection_group, $data, $expiration ) { + $mc = $this->get_connection( $connection_group ); + return $mc->set( $this->normalize_key( $key ), $data, $expiration ); + } + + /** + * Retrieve an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array{value: mixed, found: bool} + */ + public function get( $key, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + /** @psalm-suppress MixedAssignment */ + $value = $mc->get( $this->normalize_key( $key ) ); + + return [ + 'value' => $value, + 'found' => \Memcached::RES_NOTFOUND !== $mc->getResultCode(), + ]; + } + + /** + * Retrieve multiple items. + * + * @param array $keys List of keys to retrieve. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array|false Will not return anything in the array for unfound keys. + * @psalm-suppress MixedAssignment + */ + public function get_multiple( $keys, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + $mapped_keys = $this->normalize_keys_with_mapping( $keys ); + + $results = []; + if ( count( $mapped_keys ) > 1000 ) { + // Sending super large multiGets to a memcached server results in + // extremely inflated read/response buffers which can consume a lot memory perpetually. + // So instead we'll chunk up and send multiple reasonably-sized requests. + $chunked_keys = array_chunk( $mapped_keys, 1000, true ); + + foreach( $chunked_keys as $chunk_of_keys ) { + /** @psalm-var array|false $partial_results */ + $partial_results = $mc->getMulti( array_keys( $chunk_of_keys ) ); + + if ( ! is_array( $partial_results ) ) { + // If any of the lookups fail, we'll bail on the whole thing to be consistent. + return false; + } + + $results = array_merge( $results, $partial_results ); + } + } else { + /** @psalm-var array|false $results */ + $results = $mc->getMulti( array_keys( $mapped_keys ) ); + + if ( ! is_array( $results ) ) { + return false; + } + } + + $return = []; + foreach ( $results as $result_key => $result_value ) { + $original_key = isset( $mapped_keys[ $result_key ] ) ? $mapped_keys[ $result_key ] : $result_key; + $return[ $original_key ] = $result_value; + } + + return $return; + } + + /** + * Delete an item. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return boolean + */ + public function delete( $key, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + return $mc->delete( $this->normalize_key( $key ) ); + } + + /** + * Delete multiple items. + * + * @param string[] $keys Array of the full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * + * @return array + */ + public function delete_multiple( $keys, $connection_group ) { + $mc = $this->get_connection( $connection_group ); + $mapped_keys = $this->normalize_keys_with_mapping( $keys ); + + /** @psalm-var array $results */ + $results = $mc->deleteMulti( array_keys( $mapped_keys ) ); + + $return = []; + foreach ( $results as $result_key => $result_value ) { + $original_key = isset( $mapped_keys[ $result_key ] ) ? $mapped_keys[ $result_key ] : $result_key; + + // deleteMulti() returns true on success, but one of the Memcached::RES_* constants if failed. + $return[ $original_key ] = true === $result_value; + } + + return $return; + } + + /** + * Increment numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to increment the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function increment( $key, $connection_group, $offset ) { + $mc = $this->get_connection( $connection_group ); + return $mc->increment( $this->normalize_key( $key ), $offset ); + } + + /** + * Decrement numeric item's value. + * + * @param string $key The full key, including group & flush prefixes. + * @param string $connection_group The group, used to find the right connection pool. + * @param int $offset The amount by which to decrement the item's value. + * + * @return false|int The new item's value on success or false on failure. + */ + public function decrement( $key, $connection_group, $offset ) { + $mc = $this->get_connection( $connection_group ); + return $mc->decrement( $this->normalize_key( $key ), $offset ); + } + + /** + * Set a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * @param mixed $data The contents to store in the cache. + * @param int $expiration When to expire the cache contents, in seconds. + * @param ?string[] $servers_to_update Specific default servers to update, in string format of "host:port". + * + * @return void + */ + public function set_with_redundancy( $key, $data, $expiration, $servers_to_update = null ) { + $mc = $this->connections['default']; + + foreach ( $this->redundancy_server_keys as $server_string => $server_key ) { + if ( is_null( $servers_to_update ) || in_array( $server_string, $servers_to_update, true ) ) { + $mc->setByKey( $server_key, $key, $data, $expiration ); + } + } + } + + /** + * Get a key across all default memcached servers. + * + * @param string $key The full key, including group & flush prefixes. + * + * @psalm-return array Key is the server's "host:port", value is returned from Memcached. + */ + public function get_with_redundancy( $key ) { + $mc = $this->connections['default']; + + $values = []; + foreach ( $this->redundancy_server_keys as $server_string => $server_key ) { + /** @psalm-suppress MixedAssignment */ + $values[ $server_string ] = $mc->getByKey( $server_key, $key ); + } + + return $values; + } + + /* + |-------------------------------------------------------------------------- + | Utils. + |-------------------------------------------------------------------------- + */ + + /** + * @param int|string $group + * @return \Memcached + */ + private function get_connection( $group ) { + return $this->connections[ (string) $group ] ?? $this->connections['default']; + } + + /** + * Servers and configurations are persisted between requests. + * So we only want to add servers when the configuration has changed. + * + * @param string $name + * @psalm-param array $servers + * @return \Memcached + */ + private function create_connection_pool( $name, $servers ) { + $mc = new \Memcached( $name ); + + // Servers and configurations are persisted between requests. + /** @psalm-var array $existing_servers */ + $existing_servers = $mc->getServerList(); + + // Check if the servers have changed since they were registered. + $needs_refresh = count( $existing_servers ) !== count( $servers ); + foreach ( $servers as $index => $server ) { + $existing_host = $existing_servers[ $index ]['host'] ?? null; + $existing_port = $existing_servers[ $index ]['port'] ?? null; + + if ( $existing_host !== $server['host'] || $existing_port !== $server['port'] ) { + $needs_refresh = true; + } + } + + if ( $needs_refresh ) { + $mc->resetServerList(); + + $servers_to_add = []; + foreach ( $servers as $server ) { + $servers_to_add[] = [ $server['host'], $server['port'], $server['weight'] ]; + } + + $mc->addServers( $servers_to_add ); + $mc->setOptions( $this->get_config_options() ); + } + + return $mc; + } + + /** + * @param string $address + * @psalm-return array{host: string, port: int} + */ + private function parse_address( string $address ): array { + $default_port = 11211; + + if ( 'unix://' == substr( $address, 0, 7 ) ) { + // Note: This slighly differs from the memcache adapater, as memcached wants unix:// stripped off. + $host = substr( $address, 7 ); + $port = 0; + } else { + $items = explode( ':', $address, 2 ); + $host = $items[0]; + $port = isset( $items[1] ) ? intval( $items[1] ) : $default_port; + } + + return [ + 'host' => $host, + 'port' => $port, + ]; + } + + private function get_config_options(): array { + /** @psalm-suppress TypeDoesNotContainType */ + $serializer = \Memcached::HAVE_IGBINARY && extension_loaded( 'igbinary' ) ? \Memcached::SERIALIZER_IGBINARY : \Memcached::SERIALIZER_PHP; + + // TODO: Check memcached.compression_threshold / memcached.compression_factor + // These are all TBD still. + return [ + \Memcached::OPT_BINARY_PROTOCOL => false, + \Memcached::OPT_SERIALIZER => $serializer, + \Memcached::OPT_CONNECT_TIMEOUT => 1000, + \Memcached::OPT_COMPRESSION => true, + \Memcached::OPT_TCP_NODELAY => true, + ]; + } + + /** + * We want to find a unique string that, when hashed, will map to each + * of the default servers in our pool. This will allow us to + * talk with each server individually and set a key with redundancy. + * + * @psalm-return array Key is the server's "host:port", value is the server_key that accesses it. + */ + private function get_server_keys_for_redundancy(): array { + $default_pool = $this->connections['default']; + $servers = $default_pool->getServerList(); + + $server_keys = []; + for ( $i = 0; $i < 1000; $i++ ) { + $test_key = 'redundancy_key_' . $i; + + /** @psalm-var array{host: string, port: int, weight: int}|false $result */ + $result = $default_pool->getServerByKey( $test_key ); + + if ( ! $result ) { + continue; + } + + $server_string = $result['host'] . ':' . $result['port']; + if ( ! isset( $server_keys[ $server_string ] ) ) { + $server_keys[ $server_string ] = $test_key; + } + + // Keep going until every server is accounted for (capped at 1000 attempts - which is incredibly unlikely unless there are tons of servers). + if ( count( $server_keys ) === count( $servers ) ) { + break; + } + } + + return $server_keys; + } + + /** + * Memcached can only handle keys with a length of 250 or less. + * The Memcache extension automatically truncates. Memcached does not. + * Instead of truncation, which can lead to some hidden consequences, + * we can hash the string and use a shortened version when interacting + * with the Memcached servers. + * + * @param string $key + * @psalm-return string + */ + private function normalize_key( $key ) { + if ( strlen( $key ) <= 250 ) { + return $key; + } else { + return substr( $key, 0, 200 ) . ':truncated:' . md5( $key ); + } + } + + /** + * Reduce key lenths while providing a map of new_key => original_key. + * + * @param string[] $keys + * @psalm-return array + */ + private function normalize_keys_with_mapping( $keys ) { + $mapped_keys = []; + + foreach ( $keys as $key ) { + $mapped_keys[ $this->normalize_key( $key ) ] = $key; + } + + return $mapped_keys; + } +} diff --git a/includes/stats.php b/includes/stats.php new file mode 100644 index 0000000000..ec27734a7c --- /dev/null +++ b/includes/stats.php @@ -0,0 +1,381 @@ + */ + public array $stats = []; + + /** + * @psalm-var array> + */ + public array $group_ops = []; + + public float $time_total = 0; + public int $size_total = 0; + + public float $slow_op_microseconds = 0.005; // 5 ms + + private string $key_salt; + + public function __construct( string $key_salt ) { + $this->key_salt = $key_salt; + + $this->stats = [ + 'get' => 0, + 'get_local' => 0, + 'get_multi' => 0, + 'set' => 0, + 'set_local' => 0, + 'add' => 0, + 'delete' => 0, + 'delete_local' => 0, + 'slow-ops' => 0, + ]; + } + + /* + |-------------------------------------------------------------------------- + | Stat tracking. + |-------------------------------------------------------------------------- + */ + + /** + * Keep stats for a memcached operation. + * + * @param string $op The operation taking place, such as "set" or "get". + * @param string|string[] $keys The memcached key/keys involved in the operation. + * @param string $group The group the keys are in. + * @param ?int $size The size of the data invovled in the operation. + * @param ?float $time The time the operation took. + * @param string $comment Extra notes about the operation. + * + * @return void + */ + public function group_ops_stats( $op, $keys, $group, $size = null, $time = null, $comment = '' ) { + $this->increment_stat( $op ); + + // Don't keep further details about the local operations. + if ( false !== strpos( $op, '_local' ) ) { + return; + } + + if ( ! is_null( $size ) ) { + $this->size_total += $size; + } + + if ( ! is_null( $time ) ) { + $this->time_total += $time; + } + + $keys = $this->strip_memcached_keys( $keys ); + + if ( $time > $this->slow_op_microseconds && 'get_multi' !== $op ) { + $this->increment_stat( 'slow-ops' ); + + /** @psalm-var string|null $backtrace */ + $backtrace = function_exists( 'wp_debug_backtrace_summary' ) ? wp_debug_backtrace_summary() : null; // phpcs:ignore + $this->group_ops['slow-ops'][] = array( $op, $keys, $size, $time, $comment, $group, $backtrace ); + } + + $this->group_ops[ $group ][] = array( $op, $keys, $size, $time, $comment ); + } + + /** + * Increment the stat counter for a memcached operation. + * + * @param string $field The stat field/group being incremented. + * @param int $num Amount to increment by. + * + * @return void + */ + public function increment_stat( $field, $num = 1 ) { + if ( ! isset( $this->stats[ $field ] ) ) { + $this->stats[ $field ] = $num; + } else { + $this->stats[ $field ] += $num; + } + } + + /* + |-------------------------------------------------------------------------- + | Utils. + |-------------------------------------------------------------------------- + */ + + /** + * Key format: key_salt:flush_number:table_prefix:key_name + * + * We want to strip the `key_salt:flush_number` part to not leak the memcached keys. + * If `key_salt` is set we strip `'key_salt:flush_number`, otherwise just strip the `flush_number` part. + * + * @param string|string[] $keys + * @return string|string[] + */ + public function strip_memcached_keys( $keys ) { + $keys = is_array( $keys ) ? $keys : [ $keys ]; + + foreach ( $keys as $index => $value ) { + $offset = 0; + + // Strip off the key salt piece. + if ( ! empty( $this->key_salt ) ) { + $salt_piece = strpos( $value, ':' ); + $offset = false === $salt_piece ? 0 : $salt_piece + 1; + } + + // Strip off the flush number. + $flush_piece = strpos( $value, ':', $offset ); + $start = false === $flush_piece ? $offset : $flush_piece; + $keys[ $index ] = substr( $value, $start + 1 ); + } + + if ( 1 === count( $keys ) ) { + return $keys[0]; + } + + return $keys; + } + + /* + |-------------------------------------------------------------------------- + | Stats markup output. + |-------------------------------------------------------------------------- + */ + + /** + * Returns the collected raw stats. + */ + public function get_stats(): array { + $stats = [ + 'operation_counts' => $this->stats, + 'operations' => [], + 'groups' => [], + 'slow-ops' => [], + 'slow-ops-groups' => [], + 'totals' => [ + 'query_time' => $this->time_total, + 'size' => $this->size_total, + ], + ]; + + foreach ( $this->group_ops as $cache_group => $dataset ) { + $cache_group = empty( $cache_group ) ? 'default' : $cache_group; + + foreach ( $dataset as $data ) { + $operation = $data[0]; + $op = [ + 'key' => $data[1], + 'size' => $data[2], + 'time' => $data[3], + 'group' => $cache_group, + 'result' => $data[4], + ]; + + if ( 'slow-ops' === $cache_group ) { + $key = 'slow-ops'; + $groups_key = 'slow-ops-groups'; + $op['group'] = $data[5]; + $op['backtrace'] = $data[6]; + } else { + $key = 'operations'; + $groups_key = 'groups'; + } + + $stats[ $key ][ $operation ][] = $op; + if ( ! in_array( $op['group'], $stats[ $groups_key ], true ) ) { + $stats[ $groups_key ][] = $op['group']; + } + } + } + + return $stats; + } + + public function stats(): void { + $this->js_toggle(); + + $total_query_time = number_format_i18n( (float) sprintf( '%0.1f', $this->time_total * 1000 ), 1 ) . ' ms'; + + $total_size = size_format( $this->size_total, 2 ); + $total_size = false === $total_size ? '0 B' : $total_size; + + echo '

Total memcached query time:' . esc_html( $total_query_time ) . '

'; + echo "\n"; + echo '

Total memcached size:' . esc_html( $total_size ) . '

'; + echo "\n"; + + foreach ( $this->stats as $stat => $n ) { + if ( empty( $n ) ) { + continue; + } + + echo '

'; + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $this->colorize_debug_line( "$stat $n" ); + echo '

'; + } + + echo "\n"; + + echo "
\n"; + foreach ( $groups as $group ) { + $group_name = empty( $group ) ? 'default' : $group; + + $current = $active_group == $group ? 'style="display: block"' : 'style="display: none"'; + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo "
\n"; + echo '

' . esc_html( $group_titles[ $group ] ) . '

' . "\n"; + echo "
\n";
+			foreach ( $this->group_ops[ $group ] as $index => $arr ) {
+				echo esc_html( sprintf( '%3d ', $index ) );
+				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+				echo $this->get_group_ops_line( $index, $arr );
+			}
+			echo "
\n"; + echo '
'; + } + + echo '
'; + } + + public function js_toggle(): void { + echo " + + "; + } + + /** + * @param string $line + * @param string $trailing_html + * @return string + */ + public function colorize_debug_line( $line, $trailing_html = '' ) { + $colors = array( + 'get' => 'green', + 'get_local' => 'lightgreen', + 'get_multi' => 'fuchsia', + 'get_multiple' => 'navy', + 'set' => 'purple', + 'set_local' => 'orchid', + 'add' => 'blue', + 'delete' => 'red', + 'delete_local' => 'tomato', + 'slow-ops' => 'crimson', + ); + + $cmd = substr( $line, 0, (int) strpos( $line, ' ' ) ); + + // Start off with a neutral default color, and use a more specific one if possible. + $color_for_cmd = isset( $colors[ $cmd ] ) ? $colors[ $cmd ] : 'brown'; + + $cmd2 = "" . esc_html( $cmd ) . ''; + + return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "$trailing_html\n"; + } + + /** + * @param string|int $index + * @param array $arr + * @psalm-param array{0: string, 1: string|string[], 2: int|null, 3: float|null, 4: string, 5: string, 6: string|null } $arr + * + * @return string + */ + public function get_group_ops_line( $index, $arr ) { + // operation + $line = "{$arr[0]} "; + + // keys + // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode + $json_encoded_key = json_encode( $arr[1] ); + $line .= $json_encoded_key . ' '; + + // comment + if ( ! empty( $arr[4] ) ) { + $line .= "{$arr[4]} "; + } + + // size + if ( isset( $arr[2] ) ) { + $size = size_format( $arr[2], 2 ); + + if ( $size ) { + $line .= '(' . $size . ') '; + } + } + + // time + if ( isset( $arr[3] ) ) { + $line .= '(' . number_format_i18n( (float) sprintf( '%0.1f', $arr[3] * 1000 ), 1 ) . ' ms)'; + } + + // backtrace + $bt_link = ''; + if ( isset( $arr[6] ) ) { + $key_hash = md5( $index . $json_encoded_key ); + $bt_link = " Toggle Backtrace"; + $bt_link .= "'; + } + + return $this->colorize_debug_line( $line, $bt_link ); + } +} diff --git a/includes/wp-object-cache.php b/includes/wp-object-cache.php new file mode 100644 index 0000000000..0bc14dec38 --- /dev/null +++ b/includes/wp-object-cache.php @@ -0,0 +1,1128 @@ + + */ + public array $flush_number = []; + + public ?int $global_flush_number = null; + public string $global_prefix = ''; + public string $blog_prefix = ''; + public string $key_salt = ''; + + /** + * Global cache groups (network-wide rather than site-specific). + * @var string[] + */ + public array $global_groups = []; + + /** + * Non-persistent cache groups (will not write to Memcached servers). + * @var string[] + */ + public array $no_mc_groups = []; + + public int $default_expiration = 0; + public int $max_expiration = 2592000; // 30 days + + private Adapter_Interface $adapter; + private Stats $stats_helper; + + /** @psalm-var array */ + public array $mc = []; + + /** @psalm-var array */ + public array $default_mcs = []; + + /** + * @psalm-var array + */ + public array $cache = []; + + // Stats tracking. + public array $stats = []; + public array $group_ops = []; + public int $cache_hits = 0; + public int $cache_misses = 0; + public float $time_start = 0; + public float $time_total = 0; + public int $size_total = 0; + public float $slow_op_microseconds = 0.005; // 5 ms + + // TODO: Deprecate. These appear to be unused. + public string $old_flush_key = 'flush_number'; + public bool $cache_enabled = true; + public array $stats_callback = []; + /** @psalm-var array */ + public array $connection_errors = []; + + /** + * @global array>|array|null $memcached_servers + * @global string $table_prefix + * @global int|numeric-string $blog_id + * + * @param ?Adapter_Interface $adapter Optionally inject the adapter layer, useful for unit testing. + * @psalm-suppress UnsupportedReferenceUsage + */ + public function __construct( $adapter = null ) { + global $blog_id, $table_prefix, $memcached_servers; + + $this->global_groups = [ $this->global_flush_group ]; + + $is_ms = function_exists( 'is_multisite' ) && is_multisite(); + + $this->global_prefix = $is_ms || ( defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; + $this->blog_prefix = (string) ( $is_ms ? $blog_id : $table_prefix ); + + $use_memcached = defined( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION' ) && AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION; + if ( ! is_null( $adapter ) ) { + $this->adapter = $adapter; + } else { + $servers = is_array( $memcached_servers ) ? $memcached_servers : [ 'default' => [ '127.0.0.1:11211' ] ]; + $this->adapter = $use_memcached ? new Memcached_Adapter( $servers ) : new Memcache_Adapter( $servers ); + } + + $this->salt_keys( WP_CACHE_KEY_SALT, $use_memcached ); + + // Backwards compatability as these have been public properties. Ideally we deprecate and remove in the future. + $this->mc = $this->adapter->get_connections(); + $this->default_mcs = $this->adapter->get_default_connections(); + $this->connection_errors =& $this->adapter->get_connection_errors(); + + $this->stats_helper = new Stats( $this->key_salt ); + + // Also for backwards compatability since these have been public properties. + $this->stats =& $this->stats_helper->stats; + $this->group_ops =& $this->stats_helper->group_ops; + $this->time_total =& $this->stats_helper->time_total; + $this->size_total =& $this->stats_helper->size_total; + $this->slow_op_microseconds =& $this->stats_helper->slow_op_microseconds; + $this->cache_hits =& $this->stats['get']; + $this->cache_misses =& $this->stats['add']; + } + + /* + |-------------------------------------------------------------------------- + | The main methods used by the cache API. + |-------------------------------------------------------------------------- + */ + + /** + * Adds data to the cache if it doesn't already exist. + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True on success, false on failure or if cache key and group already exist. + */ + public function add( $key, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $key, $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + if ( $this->is_non_persistent_group( $group ) ) { + if ( isset( $this->cache[ $key ] ) ) { + return false; + } + + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => false, + ]; + + return true; + } + + if ( isset( $this->cache[ $key ]['value'] ) && false !== $this->cache[ $key ]['value'] ) { + return false; + } + + $expire = $this->get_expiration( $expire ); + $size = $this->get_data_size( $data ); + + $this->timer_start(); + $result = $this->adapter->add( $key, $group, $data, $expire ); + $elapsed = $this->timer_stop(); + + $comment = ''; + if ( isset( $this->cache[ $key ] ) ) { + $comment .= ' [lc already]'; + } + + if ( false === $result ) { + $comment .= ' [mc already]'; + } + + $this->group_ops_stats( 'add', $key, $group, $size, $elapsed, $comment ); + + if ( $result ) { + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => true, + ]; + } elseif ( isset( $this->cache[ $key ]['value'] ) && false === $this->cache[ $key ]['value'] ) { + /* + * Here we unset local cache if remote add failed and local cache value is equal to `false` in order + * to update the local cache anytime we get a new information from remote server. This way, the next + * cache get will go to remote server and will fetch recent data. + */ + unset( $this->cache[ $key ] ); + } + + return $result; + } + + /** + * Adds multiple values to the cache in one call. + * + * @param mixed[] $data Array of keys and values to be added. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false on failure or if cache key and group already exist. + */ + public function add_multiple( $data, $group = '', $expire = 0 ) { + $result = []; + + /** @psalm-suppress MixedAssignment - $value is unknown/mixed */ + foreach ( $data as $key => $value ) { + $result[ $key ] = $this->add( $key, $value, $group, $expire ); + } + + return $result; + } + + /** + * Replaces the contents in the cache, if contents already exist. + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True if contents were replaced, false on failure or if the original value did not exist. + */ + public function replace( $key, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $key, $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + if ( $this->is_non_persistent_group( $group ) ) { + if ( ! isset( $this->cache[ $key ] ) ) { + return false; + } + + $this->cache[ $key ]['value'] = $data; + return true; + } + + $expire = $this->get_expiration( $expire ); + $size = $this->get_data_size( $data ); + + $this->timer_start(); + $result = $this->adapter->replace( $key, $group, $data, $expire ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'replace', $key, $group, $size, $elapsed ); + + if ( $result ) { + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => true, + ]; + } else { + // Remove from local cache if the replace failed, as it may no longer exist. + unset( $this->cache[ $key ] ); + } + + return $result; + } + + /** + * Sets the data contents into the cache. + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. How long until the cahce contents will expire (in seconds). + * + * @return bool True if contents were set, false if failed. + */ + public function set( $key, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $key, $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + if ( $this->is_non_persistent_group( $group ) ) { + $this->group_ops_stats( 'set_local', $key, $group ); + + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => false, + ]; + + return true; + } + + $expire = $this->get_expiration( $expire ); + $size = $this->get_data_size( $data ); + + $this->timer_start(); + $result = $this->adapter->set( $key, $group, $data, $expire ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'set', $key, $group, $size, $elapsed ); + + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => $result, + ]; + + return $result; + } + + /** + * Sets multiple values to the cache in one call. + * + * @param mixed[] $data Array of key and value to be set. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Value is true on success, false on failure. + */ + public function set_multiple( $data, $group = '', $expire = 0 ) { + $result = []; + + /** @psalm-suppress MixedAssignment - $value is mixed */ + foreach ( $data as $key => $value ) { + // TODO: Could try to make Memcached::setMulti() work, though the return structure differs. + $result[ $key ] = $this->set( $key, $value, $group, $expire ); + } + + return $result; + } + + /** + * Retrieves the cache contents, if it exists. + * + * @param int|string $key The key under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * @param bool $force Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @param bool $found Optional. Whether the key was found in the cache (passed by reference). + * Disambiguates a return of false, a storable value. Default null. + * @return mixed|false The cache contents on success, false on failure to retrieve contents. + */ + public function get( $key, $group = 'default', $force = false, &$found = null ) { + $key = $this->key( $key, $group ); + + if ( $force && $this->is_non_persistent_group( $group ) ) { + // There's nothing to "force" retrieve. + $force = false; + } + + if ( isset( $this->cache[ $key ] ) && ! $force ) { + /** @psalm-suppress MixedAssignment */ + $value = is_object( $this->cache[ $key ]['value'] ) ? clone $this->cache[ $key ]['value'] : $this->cache[ $key ]['value']; + $found = $this->cache[ $key ]['found']; + + $this->group_ops_stats( 'get_local', $key, $group, null, null, 'local' ); + + return $value; + } + + // For a non-persistant group, if it's not in local cache then it just doesn't exist. + if ( $this->is_non_persistent_group( $group ) ) { + $found = false; + $this->group_ops_stats( 'get_local', $key, $group, null, null, 'not_in_local' ); + + return false; + } + + $this->timer_start(); + /** @psalm-suppress MixedAssignment */ + $result = $this->adapter->get( $key, $group ); + $elapsed = $this->timer_stop(); + + $this->cache[ $key ] = $result; + $found = $result['found']; + + if ( $result['found'] ) { + $this->group_ops_stats( 'get', $key, $group, $this->get_data_size( $result['value'] ), $elapsed, 'memcache' ); + } else { + $this->group_ops_stats( 'get', $key, $group, null, $elapsed, 'not_in_memcache' ); + } + + return $result['value']; + } + + /** + * Retrieves multiple values from the cache in one call. + * + * @param array $keys Array of keys under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * @param bool $force Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @return mixed[] Array of return values, grouped by key. Each value is either + * the cache contents on success, or false on failure. + */ + public function get_multiple( $keys, $group = 'default', $force = false ) { + $uncached_keys = []; + $return = []; + $return_cache = []; + + if ( $force && $this->is_non_persistent_group( $group ) ) { + // There's nothing to "force" retrieve. + $force = false; + } + + // First, fetch what we can from runtime cache. + foreach ( $keys as $key ) { + $cache_key = $this->key( $key, $group ); + + if ( isset( $this->cache[ $cache_key ] ) && ! $force ) { + /** @psalm-suppress MixedAssignment */ + $return[ $key ] = is_object( $this->cache[ $cache_key ]['value'] ) ? clone $this->cache[ $cache_key ]['value'] : $this->cache[ $cache_key ]['value']; + + $this->group_ops_stats( 'get_local', $cache_key, $group, null, null, 'local' ); + } elseif ( $this->is_non_persistent_group( $group ) ) { + $return[ $key ] = false; + $this->group_ops_stats( 'get_local', $cache_key, $group, null, null, 'not_in_local' ); + } else { + $uncached_keys[ $key ] = $cache_key; + } + } + + if ( ! empty( $uncached_keys ) ) { + $this->timer_start(); + $values = $this->adapter->get_multiple( array_values( $uncached_keys ), $group ); + $elapsed = $this->timer_stop(); + + $values = false === $values ? [] : $values; + foreach ( $uncached_keys as $key => $cache_key ) { + $found = array_key_exists( $cache_key, $values ); + /** @psalm-suppress MixedAssignment */ + $value = $found ? $values[ $cache_key ] : false; + + /** @psalm-suppress MixedAssignment */ + $return[ $key ] = $value; + $return_cache[ $cache_key ] = [ + 'value' => $value, + 'found' => $found, + ]; + } + + $this->group_ops_stats( 'get_multiple', array_values( $uncached_keys ), $group, $this->get_data_size( array_values( $values ) ), $elapsed ); + } + + $this->cache = array_merge( $this->cache, $return_cache ); + return $return; + } + + /** + * Retrieves multiple values from the cache in one call. + * + * @param array> $groups Array of keys, indexed by group. + * Example: $groups['group-name'] = [ 'key1', 'key2' ] + * + * @return mixed[] Array of return values, grouped by key. Each value is either + * the cache contents on success, or false on failure. + */ + public function get_multi( $groups ) { + $return = []; + + foreach ( $groups as $group => $keys ) { + $results = $this->get_multiple( $keys, $group ); + + foreach ( $keys as $key ) { + // This feels like a bug, as the full cache key is not useful to consumers. But alas, should be deprecating this method soon anyway. + $cache_key = $this->key( $key, $group ); + + /** @psalm-suppress MixedAssignment */ + $return[ $cache_key ] = isset( $results[ $key ] ) ? $results[ $key ] : false; + } + } + + return $return; + } + + /** + * Removes the contents of the cache key in the group. + * + * @param int|string $key What the contents in the cache are called. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * + * @return bool True on success, false on failure or if the contents were not deleted. + */ + public function delete( $key, $group = 'default' ) { + $key = $this->key( $key, $group ); + + if ( $this->is_non_persistent_group( $group ) ) { + $result = isset( $this->cache[ $key ] ); + unset( $this->cache[ $key ] ); + + return $result; + } + + $this->timer_start(); + $deleted = $this->adapter->delete( $key, $group ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'delete', $key, $group, null, $elapsed ); + + // Remove from local cache regardless of the result. + unset( $this->cache[ $key ] ); + + return $deleted; + } + + /** + * Deletes multiple values from the cache in one call. + * + * @param array $keys Array of keys to be deleted. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false if the contents were not deleted. + */ + public function delete_multiple( $keys, $group = '' ) { + if ( $this->is_non_persistent_group( $group ) ) { + $return = []; + + foreach ( $keys as $key ) { + $cache_key = $this->key( $key, $group ); + + $deleted = isset( $this->cache[ $cache_key ] ); + unset( $this->cache[ $cache_key ] ); + + $return[ $key ] = $deleted; + } + + return $return; + } + + $mapped_keys = $this->map_keys( $keys, $group ); + + $this->timer_start(); + $results = $this->adapter->delete_multiple( array_keys( $mapped_keys ), $group ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'delete_multiple', array_keys( $mapped_keys ), $group, null, $elapsed ); + + $return = []; + foreach ( $results as $cache_key => $deleted ) { + $return[ $mapped_keys[ $cache_key ] ] = $deleted; + + // Remove from local cache regardless of the result. + unset( $this->cache[ $cache_key ] ); + } + + return $return; + } + + /** + * Increments numeric cache item's value. + * + * @param int|string $key The cache key to increment. + * @param int $offset Optional. The amount by which to increment the item's value. + * Default 1. + * @param string $group Optional. The group the key is in. Default 'default'. + * @return int|false The item's new value on success, false on failure. + */ + public function incr( $key, $offset = 1, $group = 'default' ) { + $key = $this->key( $key, $group ); + + if ( $this->is_non_persistent_group( $group ) ) { + if ( ! isset( $this->cache[ $key ] ) || ! is_int( $this->cache[ $key ]['value'] ) ) { + return false; + } + + $this->cache[ $key ]['value'] += $offset; + return $this->cache[ $key ]['value']; + } + + $this->timer_start(); + $incremented = $this->adapter->increment( $key, $group, $offset ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'increment', $key, $group, null, $elapsed ); + + $this->cache[ $key ] = [ + 'value' => $incremented, + 'found' => false !== $incremented, + ]; + + return $incremented; + } + + /** + * Decrements numeric cache item's value. + * + * @param int|string $key The cache key to decrement. + * @param int $offset Optional. The amount by which to decrement the item's value. + * Default 1. + * @param string $group Optional. The group the key is in. Default 'default'. + * @return int|false The item's new value on success, false on failure. + */ + public function decr( $key, $offset = 1, $group = 'default' ) { + $key = $this->key( $key, $group ); + + if ( $this->is_non_persistent_group( $group ) ) { + if ( ! isset( $this->cache[ $key ] ) || ! is_int( $this->cache[ $key ]['value'] ) ) { + return false; + } + + $new_value = $this->cache[ $key ]['value'] - $offset; + if ( $new_value < 0 ) { + $new_value = 0; + } + + $this->cache[ $key ]['value'] = $new_value; + return $this->cache[ $key ]['value']; + } + + $this->timer_start(); + $decremented = $this->adapter->decrement( $key, $group, $offset ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'decrement', $key, $group, null, $elapsed ); + + $this->cache[ $key ] = [ + 'value' => $decremented, + 'found' => false !== $decremented, + ]; + + return $decremented; + } + + /** + * Clears the object cache of all data. + * + * Purposely does not use the memcached flush method, + * as that acts on the entire memcached server, affecting all sites. + * Instead, we rotate the key prefix for the current site, + * along with the global key when flushing the main site. + * + * @return true Always returns true. + */ + public function flush() { + $this->cache = []; + + $flush_number = $this->new_flush_number(); + + $this->rotate_site_keys( $flush_number ); + if ( is_main_site() ) { + $this->rotate_global_keys( $flush_number ); + } + + return true; + } + + /** + * Unsupported: Removes all cache items in a group. + * + * @param string $_group Name of group to remove from cache. + * @return bool Returns false, as there is no support for group flushes. + */ + public function flush_group( $_group ) { + return false; + } + + /** + * Removes all cache items from the in-memory runtime cache. + * Also reset the local stat-related tracking for individual operations. + * + * @return true Always returns true. + */ + public function flush_runtime() { + $this->cache = []; + $this->group_ops = []; + + return true; + } + + /** + * Sets the list of global cache groups. + * + * @param string|string[] $groups List of groups that are global. + * @return void + */ + public function add_global_groups( $groups ) { + if ( ! is_array( $groups ) ) { + $groups = (array) $groups; + } + + $this->global_groups = array_unique( array_merge( $this->global_groups, $groups ) ); + } + + /** + * Sets the list of non-persistent groups. + * + * @param string|string[] $groups List of groups that will not be saved to persistent cache. + * @return void + */ + public function add_non_persistent_groups( $groups ) { + if ( ! is_array( $groups ) ) { + $groups = (array) $groups; + } + + $this->no_mc_groups = array_unique( array_merge( $this->no_mc_groups, $groups ) ); + } + + /** + * Switches the internal blog ID. + * + * This changes the blog ID used to create keys in blog specific groups. + * + * @param int $blog_id Blog ID. + * @return void + */ + public function switch_to_blog( $blog_id ) { + global $table_prefix; + + /** @psalm-suppress RedundantCastGivenDocblockType **/ + $blog_id = (int) $blog_id; + + $this->blog_prefix = (string) ( is_multisite() ? $blog_id : $table_prefix ); + } + + /** + * Close the connections. + * + * @return bool + */ + public function close() { + return $this->adapter->close_connections(); + } + + + /* + |-------------------------------------------------------------------------- + | Internal methods that deal with flush numbers, the pseudo-cache-flushing mechanic. + | Public methods here may be deprecated & made private in the future. + |-------------------------------------------------------------------------- + */ + + /** + * Get the flush number prefix, used for creating the key string. + * + * @param string|int $group + * @return string + */ + public function flush_prefix( $group ) { + if ( $group === $this->flush_group || $group === $this->global_flush_group ) { + // Never flush the flush numbers. + $number = '_'; + } elseif ( false !== array_search( $group, $this->global_groups ) ) { + $number = $this->get_global_flush_number(); + } else { + $number = $this->get_blog_flush_number(); + } + + return $number . ':'; + } + + /** + * Get the global group flush number. + * + * @return int + */ + public function get_global_flush_number() { + if ( ! isset( $this->global_flush_number ) ) { + $this->global_flush_number = $this->get_flush_number( $this->global_flush_group ); + } + + return $this->global_flush_number; + } + + /** + * Get the blog's flush number. + * + * @return int + */ + public function get_blog_flush_number() { + if ( ! isset( $this->flush_number[ $this->blog_prefix ] ) ) { + $this->flush_number[ $this->blog_prefix ] = $this->get_flush_number( $this->flush_group ); + } + + return $this->flush_number[ $this->blog_prefix ]; + } + + /** + * Get the flush number for a specific group. + * + * @param string $group + * @return int + */ + public function get_flush_number( $group ) { + $flush_number = $this->get_max_flush_number( $group ); + + if ( empty( $flush_number ) ) { + // If there was no flush number anywhere, make a new one. This flushes the cache. + $flush_number = $this->new_flush_number(); + $this->set_flush_number( $flush_number, $group ); + } + + return $flush_number; + } + + /** + * Set the flush number for a specific group. + * + * @param int $value + * @param string $group + * @return void + */ + public function set_flush_number( $value, $group ) { + $key = $this->key( $this->flush_key, $group ); + $expire = 0; + $size = 19; // size of the microsecond timestamp serialized + + $this->timer_start(); + $this->adapter->set_with_redundancy( $key, $value, $expire ); + $elapsed = $this->timer_stop(); + + $average_time_elapsed = $elapsed / count( $this->default_mcs ); + foreach ( $this->default_mcs as $_default_mc ) { + $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $average_time_elapsed, 'replication' ); + } + } + + /** + * Get the highest flush number from all default servers, replicating if needed. + * + * @param string $group + * @return int|false + */ + public function get_max_flush_number( $group ) { + $key = $this->key( $this->flush_key, $group ); + $size = 19; // size of the microsecond timestamp serialized + + $this->timer_start(); + $values = $this->adapter->get_with_redundancy( $key ); + $elapsed = $this->timer_stop(); + + $replication_servers_count = max( count( $this->default_mcs ), 1 ); + $average_time_elapsed = $elapsed / $replication_servers_count; + + /** @psalm-suppress MixedAssignment */ + foreach ( $values as $result ) { + if ( false === $result ) { + $this->group_ops_stats( 'get_flush_number', $key, $group, null, $average_time_elapsed, 'not_in_memcache' ); + } else { + $this->group_ops_stats( 'get_flush_number', $key, $group, $size, $average_time_elapsed, 'memcache' ); + } + } + + $values = array_map( 'intval', $values ); + /** @psalm-suppress ArgumentTypeCoercion */ + $max = max( $values ); + + if ( $max <= 0 ) { + return false; + } + + $servers_to_update = []; + foreach ( $values as $server_string => $value ) { + if ( $value < $max ) { + $servers_to_update[] = $server_string; + } + } + + // Replicate to servers not having the max. + if ( ! empty( $servers_to_update ) ) { + $expire = 0; + + $this->timer_start(); + $this->adapter->set_with_redundancy( $key, $max, $expire, $servers_to_update ); + $elapsed = $this->timer_stop(); + + $average_time_elapsed = $elapsed / count( $servers_to_update ); + foreach ( $servers_to_update as $updated_server ) { + $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $average_time_elapsed, 'replication_repair' ); + } + } + + return $max; + } + + /** + * Rotate the flush number for the site/blog. + * + * @param ?int $flush_number + * @return void + */ + public function rotate_site_keys( $flush_number = null ) { + if ( is_null( $flush_number ) ) { + $flush_number = $this->new_flush_number(); + } + + $this->set_flush_number( $flush_number, $this->flush_group ); + $this->flush_number[ $this->blog_prefix ] = $flush_number; + } + + /** + * Rotate the global flush number. + * + * @param ?int $flush_number + * @return void + */ + public function rotate_global_keys( $flush_number = null ) { + if ( is_null( $flush_number ) ) { + $flush_number = $this->new_flush_number(); + } + + $this->set_flush_number( $flush_number, $this->global_flush_group ); + $this->global_flush_number = $flush_number; + } + + /** + * Generate new flush number. + * + * @return int + */ + public function new_flush_number(): int { + return intval( microtime( true ) * 1e6 ); + } + + /* + |-------------------------------------------------------------------------- + | Utility methods. Internal use only. + | Public methods here may be deprecated & made private in the future. + |-------------------------------------------------------------------------- + */ + + /** + * Generate the key we'll use to interact with memcached. + * Note: APCU requires this to be public right now. + * + * @param int|string $key + * @param int|string $group + * @return string + */ + public function key( $key, $group ): string { + if ( empty( $group ) ) { + $group = 'default'; + } + + $result = sprintf( + '%s%s%s:%s:%s', + $this->key_salt, + $this->flush_prefix( $group ), + array_search( $group, $this->global_groups ) !== false ? $this->global_prefix : $this->blog_prefix, + $group, + $key + ); + + return preg_replace( '/\\s+/', '', $result ); + } + + /** + * Map the full cache key to the original key. + * + * @param array $keys + * @param string $group + * @return array + */ + protected function map_keys( $keys, $group ): array { + $results = []; + + foreach ( $keys as $key ) { + $results[ $this->key( $key, $group ) ] = $key; + } + + return $results; + } + + /** + * Get the memcached instance for the specified group. + * + * @param int|string $group + * @return Memcache|Memcached + */ + public function get_mc( $group ) { + if ( isset( $this->mc[ $group ] ) ) { + return $this->mc[ $group ]; + } + + return $this->mc['default']; + } + + /** + * Sanitize the expiration time. + * + * @psalm-param int|numeric-string|float $expire + */ + private function get_expiration( $expire ): int { + $expire = intval( $expire ); + if ( $expire <= 0 || $expire > $this->max_expiration ) { + $expire = $this->default_expiration; + } + + return $expire; + } + + /** + * Check if the group is set up for non-persistent cache. + * + * @param string|int $group + * @return bool + */ + private function is_non_persistent_group( $group ) { + return in_array( $group, $this->no_mc_groups, true ); + } + + /** + * Estimate the (uncompressed) data size. + * + * @param mixed $data + * @return int + * @psalm-return 0|positive-int + */ + public function get_data_size( $data ): int { + if ( is_string( $data ) ) { + return strlen( $data ); + } + + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize + return strlen( serialize( $data ) ); + } + + /** + * Sets the key salt property. + * + * @param mixed $key_salt + * @param bool $add_mc_prefix + * @return void + */ + public function salt_keys( $key_salt, $add_mc_prefix = false ) { + $key_salt = is_string( $key_salt ) && strlen( $key_salt ) ? $key_salt : ''; + $key_salt = $add_mc_prefix ? $key_salt . '_mc' : ''; + + $this->key_salt = empty( $key_salt ) ? '' : $key_salt . ':'; + } + + public function timer_start(): bool { + $this->time_start = microtime( true ); + return true; + } + + public function timer_stop(): float { + return microtime( true ) - $this->time_start; + } + + /** + * TODO: Deprecate. + * + * @param string $host + * @param string $port + */ + public function failure_callback( $host, $port ): void { + $this->connection_errors[] = array( + 'host' => $host, + 'port' => $port, + ); + } + + /* + |-------------------------------------------------------------------------- + | Stat-related tracking & output. + | A lot of the below should be deprecated/removed in the future. + |-------------------------------------------------------------------------- + */ + + /** + * Echoes the stats of the caching operations that have taken place. + * Ideally this should be the only method left public in this section. + * + * @return void Outputs the info directly, nothing is returned. + */ + public function stats() { + $this->stats_helper->stats(); + } + + /** + * Sets the key salt property. + * + * @param string $op The operation taking place, such as "set" or "get". + * @param string|string[] $keys The memcached key/keys involved in the operation. + * @param string $group The group the keys are in. + * @param ?int $size The size of the data invovled in the operation. + * @param ?float $time The time the operation took. + * @param string $comment Extra notes about the operation. + * + * @return void + */ + public function group_ops_stats( $op, $keys, $group, $size = null, $time = null, $comment = '' ) { + $this->stats_helper->group_ops_stats( $op, $keys, $group, $size, $time, $comment ); + } + + /** + * Returns the collected raw stats. + */ + public function get_stats(): array { + return $this->stats_helper->get_stats(); + } + + /** + * @param string $field The stat field/group being incremented. + * @param int $num Amount to increment by. + */ + public function increment_stat( $field, $num = 1 ): void { + $this->stats_helper->increment_stat( $field, $num ); + } + + /** + * @param string|string[] $keys + * @return string|string[] + */ + public function strip_memcached_keys( $keys ) { + return $this->stats_helper->strip_memcached_keys( $keys ); + } + + public function js_toggle(): void { + $this->stats_helper->js_toggle(); + } + + /** + * @param string $line + * @param string $trailing_html + * @return string + */ + public function colorize_debug_line( $line, $trailing_html = '' ) { + return $this->stats_helper->colorize_debug_line( $line, $trailing_html ); + } + + /** + * @param string|int $index + * @param array $arr + * @psalm-param array{0: string, 1: string|string[], 2: int|null, 3: float|null, 4: string, 5: string, 6: string|null } $arr + * + * @return string + */ + public function get_group_ops_line( $index, $arr ) { + return $this->stats_helper->get_group_ops_line( $index, $arr ); + } +} diff --git a/object-cache.php b/object-cache.php new file mode 100644 index 0000000000..3aaebbe93a --- /dev/null +++ b/object-cache.php @@ -0,0 +1,398 @@ +add( $key, $data, $group, (int) $expire ); +} + +/** + * Adds multiple values to the cache in one call. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param array $data Array of keys and values to be set. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false if cache key and group already exist. + * @psalm-param mixed[] $data + */ +function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + /** @psalm-suppress RedundantCastGivenDocblockType */ + return $wp_object_cache->add_multiple( $data, $group, (int) $expire ); +} + +/** + * Replaces the contents of the cache with new data. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key The key for the cache data that should be replaced. + * @param mixed $data The new data to store in the cache. + * @param string $group Optional. The group for the cache data that should be replaced. + * Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True if contents were replaced, false if original value does not exist. + * @psalm-suppress RedundantCast + */ +function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->replace( $key, $data, $group, (int) $expire ); +} + +/** + * Saves the data to the cache. + * + * Differs from wp_cache_add() and wp_cache_replace() in that it will always write data. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key The cache key to use for retrieval later. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Enables the same key + * to be used across groups. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True on success, false on failure. + */ +function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + if ( ! defined( 'WP_INSTALLING' ) ) { + /** @psalm-suppress RedundantCastGivenDocblockType */ + return $wp_object_cache->set( $key, $data, $group, (int) $expire ); + } + + return $wp_object_cache->delete( $key, $group ); +} + +/** + * Sets multiple values to the cache in one call. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param array $data Array of keys and values to be set. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false on failure. + * @psalm-param mixed[] $data + */ +function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + if ( ! defined( 'WP_INSTALLING' ) ) { + /** @psalm-suppress RedundantCastGivenDocblockType */ + return $wp_object_cache->set_multiple( $data, $group, (int) $expire ); + } + + return $wp_object_cache->delete_multiple( array_keys( $data ), $group ); +} + +/** + * Retrieves the cache contents from the cache by key and group. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key The key under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param bool $force Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @param bool $found Optional. Whether the key was found in the cache (passed by reference). + * Disambiguates a return of false, a storable value. Default null. + * @return mixed|false The cache contents on success, false on failure to retrieve contents. + */ +function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { + global $wp_object_cache; + + $value = apply_filters( 'pre_wp_cache_get', false, $key, $group, $force ); + if ( false !== $value ) { + $found = true; + return $value; + } + + return $wp_object_cache->get( $key, $group, $force, $found ); +} + +/** + * Retrieves multiple values from the cache in one call. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param array $keys Array of keys under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param bool $force Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @return array Array of return values, grouped by key. Each value is either + * the cache contents on success, or false on failure. + * @psalm-param (int|string)[] $keys + * @psalm-return mixed[] + */ +function wp_cache_get_multiple( $keys, $group = '', $force = false ) { + global $wp_object_cache; + + return $wp_object_cache->get_multiple( $keys, $group, $force ); +} + +/** + * Retrieves multiple values from the cache in one call. + * TODO: Deprecate + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param array> $groups Array of keys, indexed by group. Example: $groups['group-name'] = [ 'key1', 'key2' ] + * + * @return array Array of return values, with the full cache key as the index. Each value is either the cache contents on success, or false on failure. + */ +function wp_cache_get_multi( $groups ) { + global $wp_object_cache; + + return $wp_object_cache->get_multi( $groups ); +} + +/** + * Removes the cache contents matching key and group. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key What the contents in the cache are called. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @return bool True on successful removal, false on failure. + */ +function wp_cache_delete( $key, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->delete( $key, $group ); +} + +/** + * Deletes multiple values from the cache in one call. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param array $keys Array of keys under which the cache to deleted. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false if the contents were not deleted. + * @psalm-param (int|string)[] $keys + */ +function wp_cache_delete_multiple( array $keys, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->delete_multiple( $keys, $group ); +} + +/** + * Increments numeric cache item's value. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key The key for the cache contents that should be incremented. + * @param int $offset Optional. The amount by which to increment the item's value. Default 1. + * @param string $group Optional. The group the key is in. Default empty. + * @return int|false The item's new value on success, false on failure. + */ +function wp_cache_incr( $key, $offset = 1, $group = '' ) { + global $wp_object_cache; + + /** @psalm-suppress RedundantCastGivenDocblockType */ + return $wp_object_cache->incr( $key, (int) $offset, $group ); +} + +/** + * Decrements numeric cache item's value. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int|string $key The cache key to decrement. + * @param int $offset Optional. The amount by which to decrement the item's value. Default 1. + * @param string $group Optional. The group the key is in. Default empty. + * @return int|false The item's new value on success, false on failure. + */ +function wp_cache_decr( $key, $offset = 1, $group = '' ) { + global $wp_object_cache; + + /** @psalm-suppress RedundantCastGivenDocblockType */ + return $wp_object_cache->decr( $key, (int) $offset, $group ); +} + +/** + * Removes all cache items. + * + * @see WP_Object_Cache::flush() + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @return bool True on success, false on failure. + */ +function wp_cache_flush() { + global $wp_object_cache; + + return $wp_object_cache->flush(); +} + +/** + * Removes all cache items from the in-memory runtime cache. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @return bool True on success, false on failure. + */ +function wp_cache_flush_runtime() { + global $wp_object_cache; + + return $wp_object_cache->flush_runtime(); +} + +/** + * Unsupported: Removes all cache items in a group, if the object cache implementation supports it. + * + * Before calling this function, always check for group flushing support using the + * `wp_cache_supports( 'flush_group' )` function. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param string $_group Name of group to remove from cache. + * @return bool True if group was flushed, false otherwise. + */ +function wp_cache_flush_group( $_group ) { + global $wp_object_cache; + + return $wp_object_cache->flush_group( $_group ); +} + +/** + * Determines whether the object cache implementation supports a particular feature. + * + * @param string $feature Name of the feature to check for. Possible values include: + * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple', + * 'flush_runtime', 'flush_group'. + * @return bool True if the feature is supported, false otherwise. + */ +function wp_cache_supports( $feature ) { + switch ( $feature ) { + case 'add_multiple': + case 'set_multiple': + case 'get_multiple': + case 'delete_multiple': + case 'flush_runtime': + return true; + + default: + return false; + } +} + +/** + * Closes the cache. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @return bool + */ +function wp_cache_close() { + global $wp_object_cache; + + return $wp_object_cache->close(); +} + +/** + * Adds a group or set of groups to the list of global groups. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param string|string[] $groups A group or an array of groups to add. + * @return void + */ +function wp_cache_add_global_groups( $groups ) { + global $wp_object_cache; + + $wp_object_cache->add_global_groups( $groups ); +} + +/** + * Adds a group or set of groups to the list of non-persistent groups. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param string|string[] $groups A group or an array of groups to add. + * @return void + */ +function wp_cache_add_non_persistent_groups( $groups ) { + global $wp_object_cache; + + $wp_object_cache->add_non_persistent_groups( $groups ); +} + +/** + * Switches the internal blog ID. + * This changes the blog id used to create keys in blog specific groups. + * + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param int $blog_id Site ID. + * @return void + */ +function wp_cache_switch_to_blog( $blog_id ) { + global $wp_object_cache; + + /** @psalm-suppress RedundantCastGivenDocblockType */ + $wp_object_cache->switch_to_blog( (int) $blog_id ); +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000000..89e8989919 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,29 @@ + + + Custom ruleset for VIP Go mu-plugins + + . + + /\.git/* + /vendor/* + /stubs/* + + + + + + + + + + + + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000000..02f183c33e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + ./tests/ + + + + + + ./object-cache.php + + + + + + + diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 0000000000..46cae835c1 --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000000..b4e1fe0bbc --- /dev/null +++ b/readme.md @@ -0,0 +1,39 @@ +# WP Memcached + +CAUTION: This project is a work in progress. + +This is a fork of https://github.com/Automattic/wp-memcached, but may end up merging back upstream eventually. + +- Adds support for the Memcached PHP extension +- General cleanup, along with types enforcement with Psalm. +- More unit testing, for both Memcache and Memcached extensions. + +## Usage + +1. Install this plugin somewhere in your codebase. +2. Create a file at `wp-content/object-cache.php`, with the contents being just `require_once DIR . '/path/to/wp-cache-memcached/object-cache.php`. + +This plugin aims to have full compatability with the wp-memcached plugin, and you can do those first two steps and call it a day. It will work seemlessly as a replacement. Will even keep using the same cache keys/values that are already stored. + +To additionally start using the Memcached php extension instead: + +1. Ensure the Memcached extension is installed, along with the libmemcached library. +2. Add `define( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION', true );` to your wp-config.php file. + +This will result in effectively a cache flush, after which behavior will resume per usual (though hopefully with better performance and consistency). + +## Development + +``` +composer install + +# Linting / Code standards +composer run-script phpcs +composer run-script phpcs:fix + +# Type checking +composer run-script psalm + +# Unit tests +./bin/test.sh +``` diff --git a/stubs/memcache-stubs.php b/stubs/memcache-stubs.php new file mode 100644 index 0000000000..ba99c91431 --- /dev/null +++ b/stubs/memcache-stubs.php @@ -0,0 +1,444 @@ + + * Open memcached server connection + * @link https://php.net/manual/en/memcache.connect.php + * @param string $host

+ * Point to the host where memcached is listening for connections. This parameter + * may also specify other transports like unix:///path/to/memcached.sock + * to use UNIX domain sockets, in this case port must also + * be set to 0. + *

+ * @param int $port [optional]

+ * Point to the port where memcached is listening for connections. Set this + * parameter to 0 when using UNIX domain sockets. + *

+ *

+ * Please note: port defaults to + * {@link https://php.net/manual/en/memcache.ini.php#ini.memcache.default-port memcache.default_port} + * if not specified. For this reason it is wise to specify the port + * explicitly in this method call. + *

+ * @param int $timeout [optional]

Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.

+ * @return bool

Returns TRUE on success or FALSE on failure.

+ */ + public function connect($host, $port, $timeout = 1) {} + + /** + * (PECL memcache >= 2.0.0)
+ * Add a memcached server to connection pool + * @link https://php.net/manual/en/memcache.addserver.php + * @param string $host

+ * Point to the host where memcached is listening for connections. This parameter + * may also specify other transports like unix:///path/to/memcached.sock + * to use UNIX domain sockets, in this case port must also + * be set to 0. + *

+ * @param int $port [optional]

+ * Point to the port where memcached is listening for connections. + * Set this + * parameter to 0 when using UNIX domain sockets. + *

+ *

+ * Please note: port defaults to + * memcache.default_port + * if not specified. For this reason it is wise to specify the port + * explicitly in this method call. + *

+ * @param bool $persistent [optional]

+ * Controls the use of a persistent connection. Default to TRUE. + *

+ * @param int $weight [optional]

+ * Number of buckets to create for this server which in turn control its + * probability of it being selected. The probability is relative to the + * total weight of all servers. + *

+ * @param int $timeout [optional]

+ * Value in seconds which will be used for connecting to the daemon. Think + * twice before changing the default value of 1 second - you can lose all + * the advantages of caching if your connection is too slow. + *

+ * @param int $retry_interval [optional]

+ * Controls how often a failed server will be retried, the default value + * is 15 seconds. Setting this parameter to -1 disables automatic retry. + * Neither this nor the persistent parameter has any + * effect when the extension is loaded dynamically via dl. + *

+ *

+ * Each failed connection struct has its own timeout and before it has expired + * the struct will be skipped when selecting backends to serve a request. Once + * expired the connection will be successfully reconnected or marked as failed + * for another retry_interval seconds. The typical + * effect is that each web server child will retry the connection about every + * retry_interval seconds when serving a page. + *

+ * @param bool $status [optional]

+ * Controls if the server should be flagged as online. Setting this parameter + * to FALSE and retry_interval to -1 allows a failed + * server to be kept in the pool so as not to affect the key distribution + * algorithm. Requests for this server will then failover or fail immediately + * depending on the memcache.allow_failover setting. + * Default to TRUE, meaning the server should be considered online. + *

+ * @param callable $failure_callback [optional]

+ * Allows the user to specify a callback function to run upon encountering an + * error. The callback is run before failover is attempted. The function takes + * two parameters, the hostname and port of the failed server. + *

+ * @param int $timeoutms [optional]

+ *

+ * @return bool TRUE on success or FALSE on failure. + */ + public function addServer($host, $port = 11211, $persistent = true, $weight = null, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null, $timeoutms = null) {} + + /** + * (PECL memcache >= 2.1.0)
+ * Changes server parameters and status at runtime + * @link https://secure.php.net/manual/en/memcache.setserverparams.php + * @param string $host

Point to the host where memcached is listening for connections.

. + * @param int $port [optional]

+ * Point to the port where memcached is listening for connections. + *

+ * @param int $timeout [optional]

+ * Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. + *

+ * @param int $retry_interval [optional]

+ * Controls how often a failed server will be retried, the default value + * is 15 seconds. Setting this parameter to -1 disables automatic retry. + * Neither this nor the persistent parameter has any + * effect when the extension is loaded dynamically via {@link https://secure.php.net/manual/en/function.dl.php dl()}. + *

+ * @param bool $status [optional]

+ * Controls if the server should be flagged as online. Setting this parameter + * to FALSE and retry_interval to -1 allows a failed + * server to be kept in the pool so as not to affect the key distribution + * algorithm. Requests for this server will then failover or fail immediately + * depending on the memcache.allow_failover setting. + * Default to TRUE, meaning the server should be considered online. + *

+ * @param callable $failure_callback [optional]

+ * Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. + * The function takes two parameters, the hostname and port of the failed server. + *

+ * @return bool

Returns TRUE on success or FALSE on failure.

+ */ + public function setServerParams($host, $port = 11211, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null) {} + + public function setFailureCallback() {} + + /** + * (PECL memcache >= 2.1.0)
+ * Returns server status + * @link https://php.net/manual/en/memcache.getserverstatus.php + * @param string $host Point to the host where memcached is listening for connections. + * @param int $port Point to the port where memcached is listening for connections. + * @return int Returns a the servers status. 0 if server is failed, non-zero otherwise + */ + public function getServerStatus($host, $port = 11211) {} + + public function findServer() {} + + /** + * (PECL memcache >= 0.2.0)
+ * Return version of the server + * @link https://php.net/manual/en/memcache.getversion.php + * @return string|false Returns a string of server version number or FALSE on failure. + */ + public function getVersion() {} + + /** + * (PECL memcache >= 2.0.0)
+ * Add an item to the server. If the key already exists, the value will not be added and FALSE will be returned. + * @link https://php.net/manual/en/memcache.add.php + * @param string $key The key that will be associated with the item. + * @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized. + * @param int $flag [optional]

+ * Use MEMCACHE_COMPRESSED to store the item + * compressed (uses zlib). + *

+ * @param int $expire [optional]

Expiration time of the item. + * If it's equal to zero, the item will never expire. + * You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).

+ * @return bool Returns TRUE on success or FALSE on failure. Returns FALSE if such key already exist. For the rest Memcache::add() behaves similarly to Memcache::set(). + */ + public function add($key, $var, $flag = null, $expire = null) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Stores an item var with key on the memcached server. Parameter expire is expiration time in seconds. + * If it's 0, the item never expires (but memcached server doesn't guarantee this item to be stored all the time, + * it could be deleted from the cache to make place for other items). + * You can use MEMCACHE_COMPRESSED constant as flag value if you want to use on-the-fly compression (uses zlib). + * @link https://php.net/manual/en/memcache.set.php + * @param string $key The key that will be associated with the item. + * @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized. + * @param int $flag [optional] Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib). + * @param int $expire [optional] Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days). + * @return bool Returns TRUE on success or FALSE on failure. + */ + public function set($key, $var, $flag = null, $expire = null) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Replace value of the existing item + * @link https://php.net/manual/en/memcache.replace.php + * @param string $key

The key that will be associated with the item.

+ * @param mixed $var

The variable to store. Strings and integers are stored as is, other types are stored serialized.

+ * @param int $flag [optional]

Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib).

+ * @param int $expire [optional]

Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).

+ * @return bool Returns TRUE on success or FALSE on failure. + */ + public function replace($key, $var, $flag = null, $expire = null) {} + + public function cas() {} + + public function append() {} + + /** + * @return string + */ + public function prepend() {} + + /** + * (PECL memcache >= 0.2.0)
+ * Retrieve item from the server + * @link https://php.net/manual/en/memcache.get.php + * @param string|array $key

+ * The key or array of keys to fetch. + *

+ * @param int|array &$flags [optional]

+ * If present, flags fetched along with the values will be written to this parameter. These + * flags are the same as the ones given to for example {@link https://php.net/manual/en/memcache.set.php Memcache::set()}. + * The lowest byte of the int is reserved for pecl/memcache internal usage (e.g. to indicate + * compression and serialization status). + *

+ * @return string|array|false

+ * Returns the string associated with the key or + * an array of found key-value pairs when key is an {@link https://php.net/manual/en/language.types.array.php array}. + * Returns FALSE on failure, key is not found or + * key is an empty {@link https://php.net/manual/en/language.types.array.php array}. + *

+ */ + public function get($key, &$flags = null) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Delete item from the server + * https://secure.php.net/manual/en/memcache.delete.php + * @param string $key The key associated with the item to delete. + * @param int $timeout [optional] This deprecated parameter is not supported, and defaults to 0 seconds. Do not use this parameter. + * @return bool Returns TRUE on success or FALSE on failure. + */ + public function delete($key, $timeout = 0) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Get statistics of the server + * @link https://php.net/manual/en/memcache.getstats.php + * @param string $type [optional]

+ * The type of statistics to fetch. + * Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. + * According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".

+ * @param int $slabid [optional]

+ * Used in conjunction with type set to + * cachedump to identify the slab to dump from. The cachedump + * command ties up the server and is strictly to be used for + * debugging purposes. + *

+ * @param int $limit [optional]

+ * Used in conjunction with type set to cachedump to limit the number of entries to dump. + *

+ * @return array|false Returns an associative array of server statistics or FALSE on failure. + */ + public function getStats($type = null, $slabid = null, $limit = 100) {} + + /** + * (PECL memcache >= 2.0.0)
+ * Get statistics from all servers in pool + * @link https://php.net/manual/en/memcache.getextendedstats.php + * @param string $type [optional]

The type of statistics to fetch. Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".

+ * @param int $slabid [optional]

+ * Used in conjunction with type set to + * cachedump to identify the slab to dump from. The cachedump + * command ties up the server and is strictly to be used for + * debugging purposes. + *

+ * @param int $limit Used in conjunction with type set to cachedump to limit the number of entries to dump. + * @return array|false Returns a two-dimensional associative array of server statistics or FALSE + * Returns a two-dimensional associative array of server statistics or FALSE + * on failure. + */ + public function getExtendedStats($type = null, $slabid = null, $limit = 100) {} + + /** + * (PECL memcache >= 2.0.0)
+ * Enable automatic compression of large values + * @link https://php.net/manual/en/memcache.setcompressthreshold.php + * @param int $thresold

Controls the minimum value length before attempting to compress automatically.

+ * @param float $min_saving [optional]

Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings.

+ * @return bool Returns TRUE on success or FALSE on failure. + */ + public function setCompressThreshold($thresold, $min_saving = 0.2) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Increment item's value + * @link https://php.net/manual/en/memcache.increment.php + * @param string $key Key of the item to increment. + * @param int $value [optional] increment the item by value + * @return int|false Returns new items value on success or FALSE on failure. + */ + public function increment($key, $value = 1) {} + + /** + * (PECL memcache >= 0.2.0)
+ * Decrement item's value + * @link https://php.net/manual/en/memcache.decrement.php + * @param string $key Key of the item do decrement. + * @param int $value Decrement the item by value. + * @return int|false Returns item's new value on success or FALSE on failure. + */ + public function decrement($key, $value = 1) {} + + /** + * (PECL memcache >= 0.4.0)
+ * Close memcached server connection + * @link https://php.net/manual/en/memcache.close.php + * @return bool Returns TRUE on success or FALSE on failure. + */ + public function close() {} + + /** + * (PECL memcache >= 1.0.0)
+ * Flush all existing items at the server + * @link https://php.net/manual/en/memcache.flush.php + * @return bool Returns TRUE on success or FALSE on failure. + */ + public function flush() {} +} + +/** + * Represents a connection to a set of memcache servers. + * @link https://php.net/manual/en/class.memcache.php + */ +class Memcache extends MemcachePool +{ + /** + * (PECL memcache >= 0.4.0)
+ * Open memcached server persistent connection + * @link https://php.net/manual/en/memcache.pconnect.php + * @param string $host

+ * Point to the host where memcached is listening for connections. This parameter + * may also specify other transports like unix:///path/to/memcached.sock + * to use UNIX domain sockets, in this case port must also + * be set to 0. + *

+ * @param int $port [optional]

+ * Point to the port where memcached is listening for connections. Set this + * parameter to 0 when using UNIX domain sockets. + *

+ * @param int $timeout [optional]

+ * Value in seconds which will be used for connecting to the daemon. Think + * twice before changing the default value of 1 second - you can lose all + * the advantages of caching if your connection is too slow. + *

+ * @return mixed a Memcache object or FALSE on failure. + */ + public function pconnect($host, $port, $timeout = 1) {} +} + +// string $host [, int $port [, int $timeout ]] + +/** + * (PECL memcache >= 0.2.0)
+ * Memcache::connect — Open memcached server connection + * @link https://php.net/manual/en/memcache.connect.php + * @param string $host

+ * Point to the host where memcached is listening for connections. + * This parameter may also specify other transports like + * unix:///path/to/memcached.sock to use UNIX domain sockets, + * in this case port must also be set to 0. + *

+ * @param int $port [optional]

+ * Point to the port where memcached is listening for connections. + * Set this parameter to 0 when using UNIX domain sockets. + * Note: port defaults to memcache.default_port if not specified. + * For this reason it is wise to specify the port explicitly in this method call. + *

+ * @param int $timeout [optional]

+ * Value in seconds which will be used for connecting to the daemon. + *

+ * @return bool Returns TRUE on success or FALSE on failure. + */ +function memcache_connect($host, $port, $timeout = 1) {} + +/** + * (PECL memcache >= 0.4.0) + * Memcache::pconnect — Open memcached server persistent connection + * + * @link https://php.net/manual/en/memcache.pconnect.php#example-5242 + * @param string $host + * @param int|null $port + * @param int $timeout + * @return Memcache + */ +function memcache_pconnect($host, $port = null, $timeout = 1) {} + +function memcache_add_server() {} + +function memcache_set_server_params() {} + +function memcache_set_failure_callback() {} + +function memcache_get_server_status() {} + +function memcache_get_version() {} + +function memcache_add() {} + +function memcache_set() {} + +function memcache_replace() {} + +function memcache_cas() {} + +function memcache_append() {} + +function memcache_prepend() {} + +function memcache_get() {} + +function memcache_delete() {} + +/** + * (PECL memcache >= 0.2.0)
+ * Turn debug output on/off + * @link https://php.net/manual/en/function.memcache-debug.php + * @param bool $on_off

+ * Turns debug output on if equals to TRUE. + * Turns debug output off if equals to FALSE. + *

+ * @return bool TRUE if PHP was built with --enable-debug option, otherwise + * returns FALSE. + */ +function memcache_debug($on_off) {} + +function memcache_get_stats() {} + +function memcache_get_extended_stats() {} + +function memcache_set_compress_threshold() {} + +function memcache_increment() {} + +function memcache_decrement() {} + +function memcache_close() {} + +function memcache_flush() {} + +// End of memcache v.3.0.8 diff --git a/stubs/memcached-stubs.php b/stubs/memcached-stubs.php new file mode 100644 index 0000000000..b87387aa1e --- /dev/null +++ b/stubs/memcached-stubs.php @@ -0,0 +1,1528 @@ +Enables or disables payload compression. When enabled, + * item values longer than a certain threshold (currently 100 bytes) will be + * compressed during storage and decompressed during retrieval + * transparently.

+ *

Type: boolean, default: TRUE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_COMPRESSION = -1001; + const OPT_COMPRESSION_TYPE = -1004; + + /** + *

This can be used to create a "domain" for your item keys. The value + * specified here will be prefixed to each of the keys. It cannot be + * longer than 128 characters and will reduce the + * maximum available key size. The prefix is applied only to the item keys, + * not to the server keys.

+ *

Type: string, default: "".

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_PREFIX_KEY = -1002; + + /** + *

+ * Specifies the serializer to use for serializing non-scalar values. + * The valid serializers are Memcached::SERIALIZER_PHP + * or Memcached::SERIALIZER_IGBINARY. The latter is + * supported only when memcached is configured with + * --enable-memcached-igbinary option and the + * igbinary extension is loaded. + *

+ *

Type: integer, default: Memcached::SERIALIZER_PHP.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_SERIALIZER = -1003; + + /** + *

Indicates whether igbinary serializer support is available.

+ *

Type: boolean.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HAVE_IGBINARY = 0; + + /** + *

Indicates whether JSON serializer support is available.

+ *

Type: boolean.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HAVE_JSON = 0; + + /** + *

Indicates whether msgpack serializer support is available.

+ *

Type: boolean.

+ * Available as of Memcached 3.0.0. + * @since 3.0.0 + * @link https://php.net/manual/en/memcached.constants.php + */ + const HAVE_MSGPACK = 0; + + /** + *

Indicate whether set_encoding_key is available

+ *

Type: boolean.

+ * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php, https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c#L4387 + */ + const HAVE_ENCODING = 0; + + /** + * Feature support + */ + const HAVE_SESSION = 1; + const HAVE_SASL = 0; + + /** + *

Specifies the hashing algorithm used for the item keys. The valid + * values are supplied via Memcached::HASH_* constants. + * Each hash algorithm has its advantages and its disadvantages. Go with the + * default if you don't know or don't care.

+ *

Type: integer, default: Memcached::HASH_DEFAULT

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_HASH = 2; + + /** + *

The default (Jenkins one-at-a-time) item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_DEFAULT = 0; + + /** + *

MD5 item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_MD5 = 1; + + /** + *

CRC item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_CRC = 2; + + /** + *

FNV1_64 item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_FNV1_64 = 3; + + /** + *

FNV1_64A item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_FNV1A_64 = 4; + + /** + *

FNV1_32 item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_FNV1_32 = 5; + + /** + *

FNV1_32A item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_FNV1A_32 = 6; + + /** + *

Hsieh item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_HSIEH = 7; + + /** + *

Murmur item key hashing algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const HASH_MURMUR = 8; + + /** + *

Specifies the method of distributing item keys to the servers. + * Currently supported methods are modulo and consistent hashing. Consistent + * hashing delivers better distribution and allows servers to be added to + * the cluster with minimal cache losses.

+ *

Type: integer, default: Memcached::DISTRIBUTION_MODULA.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_DISTRIBUTION = 9; + + /** + *

Modulo-based key distribution algorithm.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const DISTRIBUTION_MODULA = 0; + + /** + *

Consistent hashing key distribution algorithm (based on libketama).

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const DISTRIBUTION_CONSISTENT = 1; + const DISTRIBUTION_VIRTUAL_BUCKET = 6; + + /** + *

Enables or disables compatibility with libketama-like behavior. When + * enabled, the item key hashing algorithm is set to MD5 and distribution is + * set to be weighted consistent hashing distribution. This is useful + * because other libketama-based clients (Python, Ruby, etc.) with the same + * server configuration will be able to access the keys transparently. + *

+ *

+ * It is highly recommended to enable this option if you want to use + * consistent hashing, and it may be enabled by default in future + * releases. + *

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_LIBKETAMA_COMPATIBLE = 16; + const OPT_LIBKETAMA_HASH = 17; + const OPT_TCP_KEEPALIVE = 32; + + /** + *

Enables or disables buffered I/O. Enabling buffered I/O causes + * storage commands to "buffer" instead of being sent. Any action that + * retrieves data causes this buffer to be sent to the remote connection. + * Quitting the connection or closing down the connection will also cause + * the buffered data to be pushed to the remote connection.

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_BUFFER_WRITES = 10; + + /** + *

Enable the use of the binary protocol. Please note that you cannot + * toggle this option on an open connection.

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_BINARY_PROTOCOL = 18; + + /** + *

Enables or disables asynchronous I/O. This is the fastest transport + * available for storage functions.

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_NO_BLOCK = 0; + + /** + *

Enables or disables the no-delay feature for connecting sockets (may + * be faster in some environments).

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_TCP_NODELAY = 1; + + /** + *

The maximum socket send buffer in bytes.

+ *

Type: integer, default: varies by platform/kernel + * configuration.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_SOCKET_SEND_SIZE = 4; + + /** + *

The maximum socket receive buffer in bytes.

+ *

Type: integer, default: varies by platform/kernel + * configuration.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_SOCKET_RECV_SIZE = 5; + + /** + *

In non-blocking mode this set the value of the timeout during socket + * connection, in milliseconds.

+ *

Type: integer, default: 1000.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_CONNECT_TIMEOUT = 14; + + /** + *

The amount of time, in seconds, to wait until retrying a failed + * connection attempt.

+ *

Type: integer, default: 0.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_RETRY_TIMEOUT = 15; + + /** + *

Socket sending timeout, in microseconds. In cases where you cannot + * use non-blocking I/O this will allow you to still have timeouts on the + * sending of data.

+ *

Type: integer, default: 0.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_SEND_TIMEOUT = 19; + + /** + *

Socket reading timeout, in microseconds. In cases where you cannot + * use non-blocking I/O this will allow you to still have timeouts on the + * reading of data.

+ *

Type: integer, default: 0.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_RECV_TIMEOUT = 20; + + /** + *

Timeout for connection polling, in milliseconds.

+ *

Type: integer, default: 1000.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_POLL_TIMEOUT = 8; + + /** + *

Enables or disables caching of DNS lookups.

+ *

Type: boolean, default: FALSE.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_CACHE_LOOKUPS = 6; + + /** + *

Specifies the failure limit for server connection attempts. The + * server will be removed after this many continuous connection + * failures.

+ *

Type: integer, default: 0.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const OPT_SERVER_FAILURE_LIMIT = 21; + const OPT_AUTO_EJECT_HOSTS = 28; + const OPT_HASH_WITH_PREFIX_KEY = 25; + const OPT_NOREPLY = 26; + const OPT_SORT_HOSTS = 12; + const OPT_VERIFY_KEY = 13; + const OPT_USE_UDP = 27; + const OPT_NUMBER_OF_REPLICAS = 29; + const OPT_RANDOMIZE_REPLICA_READ = 30; + const OPT_CORK = 31; + const OPT_REMOVE_FAILED_SERVERS = 35; + const OPT_DEAD_TIMEOUT = 36; + const OPT_SERVER_TIMEOUT_LIMIT = 37; + const OPT_MAX = 38; + const OPT_IO_BYTES_WATERMARK = 23; + const OPT_IO_KEY_PREFETCH = 24; + const OPT_IO_MSG_WATERMARK = 22; + const OPT_LOAD_FROM_FILE = 34; + const OPT_SUPPORT_CAS = 7; + const OPT_TCP_KEEPIDLE = 33; + const OPT_USER_DATA = 11; + + + /** + * libmemcached result codes + */ + /** + *

The operation was successful.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_SUCCESS = 0; + + /** + *

The operation failed in some fashion.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_FAILURE = 1; + + /** + *

DNS lookup failed.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_HOST_LOOKUP_FAILURE = 2; + + /** + *

Failed to read network data.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_UNKNOWN_READ_FAILURE = 7; + + /** + *

Bad command in memcached protocol.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_PROTOCOL_ERROR = 8; + + /** + *

Error on the client side.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_CLIENT_ERROR = 9; + + /** + *

Error on the server side.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_SERVER_ERROR = 10; + + /** + *

Failed to write network data.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_WRITE_FAILURE = 5; + + /** + *

Failed to do compare-and-swap: item you are trying to store has been + * modified since you last fetched it.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_DATA_EXISTS = 12; + + /** + *

Item was not stored: but not because of an error. This normally + * means that either the condition for an "add" or a "replace" command + * wasn't met, or that the item is in a delete queue.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_NOTSTORED = 14; + + /** + *

Item with this key was not found (with "get" operation or "cas" + * operations).

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_NOTFOUND = 16; + + /** + *

Partial network data read error.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_PARTIAL_READ = 18; + + /** + *

Some errors occurred during multi-get.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_SOME_ERRORS = 19; + + /** + *

Server list is empty.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_NO_SERVERS = 20; + + /** + *

End of result set.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_END = 21; + + /** + *

System error.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_ERRNO = 26; + + /** + *

The operation was buffered.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_BUFFERED = 32; + + /** + *

The operation timed out.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_TIMEOUT = 31; + + /** + *

Bad key.

+ * @link https://php.net/manual/en/memcached.constants.php, http://docs.libmemcached.org/index.html + */ + /** + *

MEMCACHED_BAD_KEY_PROVIDED: The key provided is not a valid key.

+ */ + const RES_BAD_KEY_PROVIDED = 33; + /** + *

MEMCACHED_STORED: The requested object has been successfully stored on the server.

+ */ + const RES_STORED = 15; + /** + *

MEMCACHED_DELETED: The object requested by the key has been deleted.

+ */ + const RES_DELETED = 22; + /** + *

MEMCACHED_STAT: A “stat” command has been returned in the protocol.

+ */ + const RES_STAT = 24; + /** + *

MEMCACHED_ITEM: An item has been fetched (this is an internal error only).

+ */ + const RES_ITEM = 25; + /** + *

MEMCACHED_NOT_SUPPORTED: The given method is not supported in the server.

+ */ + const RES_NOT_SUPPORTED = 28; + /** + *

MEMCACHED_FETCH_NOTFINISHED: A request has been made, but the server has not finished the fetch of the last request.

+ */ + const RES_FETCH_NOTFINISHED = 30; + /** + *

MEMCACHED_SERVER_MARKED_DEAD: The requested server has been marked dead.

+ */ + const RES_SERVER_MARKED_DEAD = 35; + /** + *

MEMCACHED_UNKNOWN_STAT_KEY: The server you are communicating with has a stat key which has not be defined in the protocol.

+ */ + const RES_UNKNOWN_STAT_KEY = 36; + /** + *

MEMCACHED_INVALID_HOST_PROTOCOL: The server you are connecting too has an invalid protocol. Most likely you are connecting to an older server that does not speak the binary protocol.

+ */ + const RES_INVALID_HOST_PROTOCOL = 34; + /** + *

MEMCACHED_MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory.

+ */ + const RES_MEMORY_ALLOCATION_FAILURE = 17; + /** + *

MEMCACHED_E2BIG: Item is too large for the server to store.

+ */ + const RES_E2BIG = 37; + /** + *

MEMCACHED_KEY_TOO_BIG: The key that has been provided is too large for the given server.

+ */ + const RES_KEY_TOO_BIG = 39; + /** + *

MEMCACHED_SERVER_TEMPORARILY_DISABLED

+ */ + const RES_SERVER_TEMPORARILY_DISABLED = 47; + /** + *

MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory. + * + * #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000008

+ */ + const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48; + /** + *

MEMCACHED_AUTH_PROBLEM: An unknown issue has occured during authentication.

+ */ + const RES_AUTH_PROBLEM = 40; + /** + *

MEMCACHED_AUTH_FAILURE: The credentials provided are not valid for this server.

+ */ + const RES_AUTH_FAILURE = 41; + /** + *

MEMCACHED_AUTH_CONTINUE: Authentication has been paused.

+ */ + const RES_AUTH_CONTINUE = 42; + /** + *

MEMCACHED_CONNECTION_FAILURE: A unknown error has occured while trying to connect to a server.

+ */ + const RES_CONNECTION_FAILURE = 3; + /** + *

MEMCACHED_CONNECTION_BIND_FAILURE: Deprecated since version <0.30(libmemcached). + * We were not able to bind() to the socket.

+ */ + const RES_CONNECTION_BIND_FAILURE = 4; + /** + *

MEMCACHED_READ_FAILURE: A read failure has occurred.

+ */ + const RES_READ_FAILURE = 6; + /** + *

MEMCACHED_DATA_DOES_NOT_EXIST: The data requested with the key given was not found.

+ */ + const RES_DATA_DOES_NOT_EXIST = 13; + /** + *

MEMCACHED_VALUE: A value has been returned from the server (this is an internal condition only).

+ */ + const RES_VALUE = 23; + /** + *

MEMCACHED_FAIL_UNIX_SOCKET: A connection was not established with the server via a unix domain socket.

+ */ + const RES_FAIL_UNIX_SOCKET = 27; + /** + *

MEMCACHED_NO_KEY_PROVIDED: Deprecated since version <0.30(libmemcached): Use MEMCACHED_BAD_KEY_PROVIDED instead. + * No key was provided.

+ */ + const RES_NO_KEY_PROVIDED = 29; + /** + *

MEMCACHED_INVALID_ARGUMENTS: The arguments supplied to the given function were not valid.

+ */ + const RES_INVALID_ARGUMENTS = 38; + /** + *

MEMCACHED_PARSE_ERROR: An error has occurred while trying to parse the configuration string. You should use memparse to determine what the error was.

+ */ + const RES_PARSE_ERROR = 43; + /** + *

MEMCACHED_PARSE_USER_ERROR: An error has occurred in parsing the configuration string.

+ */ + const RES_PARSE_USER_ERROR = 44; + /** + *

MEMCACHED_DEPRECATED: The method that was requested has been deprecated.

+ */ + const RES_DEPRECATED = 45; + //unknow + const RES_IN_PROGRESS = 46; + /** + *

MEMCACHED_MAXIMUM_RETURN: This in an internal only state.

+ */ + const RES_MAXIMUM_RETURN = 49; + + /** + * Server callbacks, if compiled with --memcached-protocol + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php + */ + const ON_CONNECT = 0; + const ON_ADD = 1; + const ON_APPEND = 2; + const ON_DECREMENT = 3; + const ON_DELETE = 4; + const ON_FLUSH = 5; + const ON_GET = 6; + const ON_INCREMENT = 7; + const ON_NOOP = 8; + const ON_PREPEND = 9; + const ON_QUIT = 10; + const ON_REPLACE = 11; + const ON_SET = 12; + const ON_STAT = 13; + const ON_VERSION = 14; + /** + * Constants used when compiled with --memcached-protocol + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php + */ + const RESPONSE_SUCCESS = 0; + const RESPONSE_KEY_ENOENT = 1; + const RESPONSE_KEY_EEXISTS = 2; + const RESPONSE_E2BIG = 3; + const RESPONSE_EINVAL = 4; + const RESPONSE_NOT_STORED = 5; + const RESPONSE_DELTA_BADVAL = 6; + const RESPONSE_NOT_MY_VBUCKET = 7; + const RESPONSE_AUTH_ERROR = 32; + const RESPONSE_AUTH_CONTINUE = 33; + const RESPONSE_UNKNOWN_COMMAND = 129; + const RESPONSE_ENOMEM = 130; + const RESPONSE_NOT_SUPPORTED = 131; + const RESPONSE_EINTERNAL = 132; + const RESPONSE_EBUSY = 133; + const RESPONSE_ETMPFAIL = 134; + + + /** + *

Failed to create network socket.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11; + + /** + *

Payload failure: could not compress/decompress or serialize/unserialize the value.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const RES_PAYLOAD_FAILURE = -1001; + + /** + *

The default PHP serializer.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const SERIALIZER_PHP = 1; + + /** + *

The igbinary serializer. + * Instead of textual representation it stores PHP data structures in a + * compact binary form, resulting in space and time gains.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const SERIALIZER_IGBINARY = 2; + + /** + *

The JSON serializer. Requires PHP 5.2.10+.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const SERIALIZER_JSON = 3; + const SERIALIZER_JSON_ARRAY = 4; + /** + *

The msgpack serializer.

+ * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php + */ + const SERIALIZER_MSGPACK = 5; + + const COMPRESSION_FASTLZ = 2; + const COMPRESSION_ZLIB = 1; + + /** + *

A flag for Memcached::getMulti and + * Memcached::getMultiByKey to ensure that the keys are + * returned in the same order as they were requested in. Non-existing keys + * get a default value of NULL.

+ * @link https://php.net/manual/en/memcached.constants.php + */ + const GET_PRESERVE_ORDER = 1; + + /** + * A flag for Memcached::get(), Memcached::getMulti() and + * Memcached::getMultiByKey() to ensure that the CAS token values are returned as well. + * @link https://php.net/manual/en/memcached.constants.php + */ + const GET_EXTENDED = 2; + + const GET_ERROR_RETURN_VALUE = false; + + /** + * (PECL memcached >= 0.1.0)
+ * Create a Memcached instance + * @link https://php.net/manual/en/memcached.construct.php, https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @param string $persistent_id [optional] + * @param callable $on_new_object_cb [optional] + * @param string $connection_str [optional] + */ + public function __construct ($persistent_id = '', $on_new_object_cb = null, $connection_str = '') {} + + /** + * (PECL memcached >= 0.1.0)
+ * Return the result code of the last operation + * @link https://php.net/manual/en/memcached.getresultcode.php + * @return int Result code of the last Memcached operation. + */ + public function getResultCode () {} + + /** + * (PECL memcached >= 1.0.0)
+ * Return the message describing the result of the last operation + * @link https://php.net/manual/en/memcached.getresultmessage.php + * @return string Message describing the result of the last Memcached operation. + */ + public function getResultMessage () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Retrieve an item + * @link https://php.net/manual/en/memcached.get.php + * @param string $key

+ * The key of the item to retrieve. + *

+ * @param callable $cache_cb [optional]

+ * Read-through caching callback or NULL. + *

+ * @param int $flags [optional]

+ * The flags for the get operation. + *

+ * @return mixed the value stored in the cache or FALSE otherwise. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function get ($key, callable $cache_cb = null, $flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Retrieve an item from a specific server + * @link https://php.net/manual/en/memcached.getbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key of the item to fetch. + *

+ * @param callable $cache_cb [optional]

+ * Read-through caching callback or NULL + *

+ * @param int $flags [optional]

+ * The flags for the get operation. + *

+ * @return mixed the value stored in the cache or FALSE otherwise. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function getByKey ($server_key, $key, callable $cache_cb = null, $flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Retrieve multiple items + * @link https://php.net/manual/en/memcached.getmulti.php + * @param array $keys

+ * Array of keys to retrieve. + *

+ * @param int $flags [optional]

+ * The flags for the get operation. + *

+ * @return mixed the array of found items or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function getMulti (array $keys, $flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Retrieve multiple items from a specific server + * @link https://php.net/manual/en/memcached.getmultibykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param array $keys

+ * Array of keys to retrieve. + *

+ * @param int $flags [optional]

+ * The flags for the get operation. + *

+ * @return array|false the array of found items or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function getMultiByKey ($server_key, array $keys, $flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Request multiple items + * @link https://php.net/manual/en/memcached.getdelayed.php + * @param array $keys

+ * Array of keys to request. + *

+ * @param bool $with_cas [optional]

+ * Whether to request CAS token values also. + *

+ * @param callable $value_cb [optional]

+ * The result callback or NULL. + *

+ * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function getDelayed (array $keys, $with_cas = null, callable $value_cb = null) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Request multiple items from a specific server + * @link https://php.net/manual/en/memcached.getdelayedbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param array $keys

+ * Array of keys to request. + *

+ * @param bool $with_cas [optional]

+ * Whether to request CAS token values also. + *

+ * @param callable $value_cb [optional]

+ * The result callback or NULL. + *

+ * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function getDelayedByKey ($server_key, array $keys, $with_cas = null, callable $value_cb = null) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Fetch the next result + * @link https://php.net/manual/en/memcached.fetch.php + * @return array|false the next result or FALSE otherwise. + * The Memcached::getResultCode will return + * Memcached::RES_END if result set is exhausted. + */ + public function fetch () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Fetch all the remaining results + * @link https://php.net/manual/en/memcached.fetchall.php + * @return array|false the results or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function fetchAll () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Store an item + * @link https://php.net/manual/en/memcached.set.php + * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function set ($key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Store an item on a specific server + * @link https://php.net/manual/en/memcached.setbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function setByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Set a new expiration on an item + * @link https://php.net/manual/en/memcached.touch.php + * @param string $key

+ * The key under which to store the value. + *

+ * @param int $expiration

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function touch ($key, $expiration = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Set a new expiration on an item on a specific server + * @link https://php.net/manual/en/memcached.touchbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param int $expiration

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function touchByKey ($server_key, $key, $expiration) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Store multiple items + * @link https://php.net/manual/en/memcached.setmulti.php + * @param array $items

+ * An array of key/value pairs to store on the server. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function setMulti (array $items, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Store multiple items on a specific server + * @link https://php.net/manual/en/memcached.setmultibykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param array $items

+ * An array of key/value pairs to store on the server. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function setMultiByKey ($server_key, array $items, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Compare and swap an item + * @link https://php.net/manual/en/memcached.cas.php + * @param float $cas_token

+ * Unique value associated with the existing item. Generated by memcache. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_DATA_EXISTS if the item you are trying + * to store has been modified since you last fetched it. + */ + public function cas ($cas_token, $key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Compare and swap an item on a specific server + * @link https://php.net/manual/en/memcached.casbykey.php + * @param float $cas_token

+ * Unique value associated with the existing item. Generated by memcache. + *

+ * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_DATA_EXISTS if the item you are trying + * to store has been modified since you last fetched it. + */ + public function casByKey ($cas_token, $server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Add an item under a new key + * @link https://php.net/manual/en/memcached.add.php + * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key already exists. + */ + public function add ($key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Add an item under a new key on a specific server + * @link https://php.net/manual/en/memcached.addbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key already exists. + */ + public function addByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Append data to an existing item + * @link https://php.net/manual/en/memcached.append.php + * @param string $key

+ * The key under which to store the value. + *

+ * @param string $value

+ * The string to append. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function append ($key, $value) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Append data to an existing item on a specific server + * @link https://php.net/manual/en/memcached.appendbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param string $value

+ * The string to append. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function appendByKey ($server_key, $key, $value) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Prepend data to an existing item + * @link https://php.net/manual/en/memcached.prepend.php + * @param string $key

+ * The key of the item to prepend the data to. + *

+ * @param string $value

+ * The string to prepend. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function prepend ($key, $value) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Prepend data to an existing item on a specific server + * @link https://php.net/manual/en/memcached.prependbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key of the item to prepend the data to. + *

+ * @param string $value

+ * The string to prepend. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function prependByKey ($server_key, $key, $value) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Replace the item under an existing key + * @link https://php.net/manual/en/memcached.replace.php + * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function replace ($key, $value, $expiration = null, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Replace the item under an existing key on a specific server + * @link https://php.net/manual/en/memcached.replacebykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key under which to store the value. + *

+ * @param mixed $value

+ * The value to store. + *

+ * @param int $expiration [optional]

+ * The expiration time, defaults to 0. See Expiration Times for more info. + *

+ * @param int $udf_flags [optional] + * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTSTORED if the key does not exist. + */ + public function replaceByKey ($server_key, $key, $value, $expiration = null, $udf_flags = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Delete an item + * @link https://php.net/manual/en/memcached.delete.php + * @param string $key

+ * The key to be deleted. + *

+ * @param int $time [optional]

+ * The amount of time the server will wait to delete the item. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function delete ($key, $time = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Delete multiple items + * @link https://php.net/manual/en/memcached.deletemulti.php + * @param array $keys

+ * The keys to be deleted. + *

+ * @param int $time [optional]

+ * The amount of time the server will wait to delete the items. + *

+ * @return array Returns array indexed by keys and where values are indicating whether operation succeeded or not. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function deleteMulti (array $keys, $time = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Delete an item from a specific server + * @link https://php.net/manual/en/memcached.deletebykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key to be deleted. + *

+ * @param int $time [optional]

+ * The amount of time the server will wait to delete the item. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function deleteByKey ($server_key, $key, $time = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Delete multiple items from a specific server + * @link https://php.net/manual/en/memcached.deletemultibykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param array $keys

+ * The keys to be deleted. + *

+ * @param int $time [optional]

+ * The amount of time the server will wait to delete the items. + *

+ * @return bool TRUE on success or FALSE on failure. + * The Memcached::getResultCode will return + * Memcached::RES_NOTFOUND if the key does not exist. + */ + public function deleteMultiByKey ($server_key, array $keys, $time = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Increment numeric item's value + * @link https://php.net/manual/en/memcached.increment.php + * @param string $key

+ * The key of the item to increment. + *

+ * @param int $offset [optional]

+ * The amount by which to increment the item's value. + *

+ * @param int $initial_value [optional]

+ * The value to set the item to if it doesn't currently exist. + *

+ * @param int $expiry [optional]

+ * The expiry time to set on the item. + *

+ * @return int|false new item's value on success or FALSE on failure. + */ + public function increment ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Decrement numeric item's value + * @link https://php.net/manual/en/memcached.decrement.php + * @param string $key

+ * The key of the item to decrement. + *

+ * @param int $offset [optional]

+ * The amount by which to decrement the item's value. + *

+ * @param int $initial_value [optional]

+ * The value to set the item to if it doesn't currently exist. + *

+ * @param int $expiry [optional]

+ * The expiry time to set on the item. + *

+ * @return int|false item's new value on success or FALSE on failure. + */ + public function decrement ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Increment numeric item's value, stored on a specific server + * @link https://php.net/manual/en/memcached.incrementbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key of the item to increment. + *

+ * @param int $offset [optional]

+ * The amount by which to increment the item's value. + *

+ * @param int $initial_value [optional]

+ * The value to set the item to if it doesn't currently exist. + *

+ * @param int $expiry [optional]

+ * The expiry time to set on the item. + *

+ * @return int|false new item's value on success or FALSE on failure. + */ + public function incrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Decrement numeric item's value, stored on a specific server + * @link https://php.net/manual/en/memcached.decrementbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @param string $key

+ * The key of the item to decrement. + *

+ * @param int $offset [optional]

+ * The amount by which to decrement the item's value. + *

+ * @param int $initial_value [optional]

+ * The value to set the item to if it doesn't currently exist. + *

+ * @param int $expiry [optional]

+ * The expiry time to set on the item. + *

+ * @return int|false item's new value on success or FALSE on failure. + */ + public function decrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Add a server to the server pool + * @link https://php.net/manual/en/memcached.addserver.php + * @param string $host

+ * The hostname of the memcache server. If the hostname is invalid, data-related + * operations will set + * Memcached::RES_HOST_LOOKUP_FAILURE result code. + *

+ * @param int $port

+ * The port on which memcache is running. Usually, this is + * 11211. + *

+ * @param int $weight [optional]

+ * The weight of the server relative to the total weight of all the + * servers in the pool. This controls the probability of the server being + * selected for operations. This is used only with consistent distribution + * option and usually corresponds to the amount of memory available to + * memcache on that server. + *

+ * @return bool TRUE on success or FALSE on failure. + */ + public function addServer ($host, $port, $weight = 0) {} + + /** + * (PECL memcached >= 0.1.1)
+ * Add multiple servers to the server pool + * @link https://php.net/manual/en/memcached.addservers.php + * @param array $servers + * @return bool TRUE on success or FALSE on failure. + */ + public function addServers (array $servers) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Get the list of the servers in the pool + * @link https://php.net/manual/en/memcached.getserverlist.php + * @return array The list of all servers in the server pool. + */ + public function getServerList () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Map a key to a server + * @link https://php.net/manual/en/memcached.getserverbykey.php + * @param string $server_key

+ * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. + *

+ * @return array an array containing three keys of host, + * port, and weight on success or FALSE + * on failure. + * Use Memcached::getResultCode if necessary. + */ + public function getServerByKey ($server_key) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Clears all servers from the server list + * @link https://php.net/manual/en/memcached.resetserverlist.php + * @return bool TRUE on success or FALSE on failure. + */ + public function resetServerList () {} + + /** + * (PECL memcached >= 2.0.0)
+ * Close any open connections + * @link https://php.net/manual/en/memcached.quit.php + * @return bool TRUE on success or FALSE on failure. + */ + public function quit () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Get server pool statistics + * @link https://php.net/manual/en/memcached.getstats.php + * @param string $type

items, slabs, sizes ...

+ * @return array Array of server statistics, one entry per server. + */ + public function getStats ($type = null) {} + + /** + * (PECL memcached >= 0.1.5)
+ * Get server pool version info + * @link https://php.net/manual/en/memcached.getversion.php + * @return array Array of server versions, one entry per server. + */ + public function getVersion () {} + + /** + * (PECL memcached >= 2.0.0)
+ * Gets the keys stored on all the servers + * @link https://php.net/manual/en/memcached.getallkeys.php + * @return array|false the keys stored on all the servers on success or FALSE on failure. + */ + public function getAllKeys () {} + + /** + * (PECL memcached >= 0.1.0)
+ * Invalidate all items in the cache + * @link https://php.net/manual/en/memcached.flush.php + * @param int $delay [optional]

+ * Numer of seconds to wait before invalidating the items. + *

+ * @return bool TRUE on success or FALSE on failure. + * Use Memcached::getResultCode if necessary. + */ + public function flush ($delay = 0) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Retrieve a Memcached option value + * @link https://php.net/manual/en/memcached.getoption.php + * @param int $option

+ * One of the Memcached::OPT_* constants. + *

+ * @return mixed the value of the requested option, or FALSE on + * error. + */ + public function getOption ($option) {} + + /** + * (PECL memcached >= 0.1.0)
+ * Set a Memcached option + * @link https://php.net/manual/en/memcached.setoption.php + * @param int $option + * @param mixed $value + * @return bool TRUE on success or FALSE on failure. + */ + public function setOption ($option, $value) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Set Memcached options + * @link https://php.net/manual/en/memcached.setoptions.php + * @param array $options

+ * An associative array of options where the key is the option to set and + * the value is the new value for the option. + *

+ * @return bool TRUE on success or FALSE on failure. + */ + public function setOptions (array $options) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Set the credentials to use for authentication + * @link https://www.php.net/manual/en/memcached.setsaslauthdata.php + * @param string $username

+ * The username to use for authentication. + *

+ * @param string $password

+ * The password to use for authentication. + *

+ * @return bool TRUE on success or FALSE on failure. + */ + public function setSaslAuthData (string $username , string $password) {} + + /** + * (PECL memcached >= 2.0.0)
+ * Check if a persitent connection to memcache is being used + * @link https://php.net/manual/en/memcached.ispersistent.php + * @return bool true if Memcache instance uses a persistent connection, false otherwise. + */ + public function isPersistent () {} + + /** + * (PECL memcached >= 2.0.0)
+ * Check if the instance was recently created + * @link https://php.net/manual/en/memcached.ispristine.php + * @return bool the true if instance is recently created, false otherwise. + */ + public function isPristine () {} + + /** + * Flush and send buffered commands + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @return bool + */ + public function flushBuffers () {} + + /** + * Sets AES encryption key (libmemcached 1.0.6 and higher) + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @param string $key + * @return bool + */ + public function setEncodingKey ( $key ) {} + + /** + * Returns the last disconnected server. Was added in 0.34 according to libmemcached's Changelog + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @return array|false + */ + public function getLastDisconnectedServer () {} + + /** + * Returns the last error errno that occurred + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @return int + */ + public function getLastErrorErrno () {} + + /** + * Returns the last error code that occurred + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @return int + */ + public function getLastErrorCode () {} + + /** + * Returns the last error message that occurred + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @return string + */ + public function getLastErrorMessage () {} + + /** + * Sets the memcached virtual buckets + * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c + * @param array $host_map + * @param array $forward_map + * @param int $replicas + * @return bool + */ + public function setBucket (array $host_map, array $forward_map, $replicas) {} + +} + +/** + * @link https://php.net/manual/en/class.memcachedexception.php + */ +class MemcachedException extends RuntimeException { + function __construct( $errmsg = "", $errcode = 0 ) {} +} +// End of memcached v.3.1.5 +?> diff --git a/stubs/wordpress-stubs.php b/stubs/wordpress-stubs.php new file mode 100644 index 0000000000..5b49aafd81 --- /dev/null +++ b/stubs/wordpress-stubs.php @@ -0,0 +1,130 @@ +`, `&`, and fixes line endings. + * + * Escapes text strings for echoing in JS. It is intended to be used for inline JS + * (in a tag attribute, for example `onclick="..."`). Note that the strings have to + * be in single quotes. The {@see 'js_escape'} filter is also applied here. + * + * @since 2.8.0 + * + * @param string $text The text to be escaped. + * @return string Escaped text. + */ +function esc_js($text) {} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000000..e9967159a7 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,34 @@ +object_cache = new WP_Object_Cache(); // phpcs:ignore + + if ( defined( 'AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION' ) && AUTOMATTIC_MEMCACHED_USE_MEMCACHED_EXTENSION ) { + $this->is_using_memcached_ext = true; + } + } + + public function tearDown(): void { + $this->object_cache->flush(); + $this->object_cache->close(); + parent::tearDown(); + } + + /* + |-------------------------------------------------------------------------- + | The main methods used by the cache API. + |-------------------------------------------------------------------------- + */ + + /** + * @dataProvider data_cache_inputs + */ + public function test_add( $value ) { + // Add to memcached. + self::assertTrue( $this->object_cache->add( 'key', $value ) ); + + // Check local cache. + $cache_key = $this->object_cache->key( 'key', 'default' ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $value, + 'found' => true, + ] ); + + // Fails to add now because it already exists in local cache. + self::assertFalse( $this->object_cache->add( 'key', $value ) ); + + // Still fails after removing from local cache because it exists in memcached. + unset( $this->object_cache->cache[ $cache_key ] ); + self::assertFalse( $this->object_cache->add( 'key', $value ) ); + + // TODO: Test unsetting from local cache after memcached failure. + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_add_for_non_persistent_groups( $value ) { + $group = 'do-not-persist-me'; + + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Add to local cache. + self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); + + // Check local cache. + $cache_key = $this->object_cache->key( 'key', $group ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $value, + 'found' => false, + ] ); + + // Fails to add now because it already exists in local cache. + self::assertFalse( $this->object_cache->add( 'key', $value, $group ) ); + + // Succeeds after removing from local cache because it never existed remotely. + unset( $this->object_cache->cache[ $cache_key ] ); + self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); + } + + public function test_add_multiple() { + $inputs = $this->data_cache_inputs(); + + $values = []; + $expected_first = []; + $expected_second = []; + foreach ( $inputs as $key => $input_array ) { + $values[ $key ] = $input_array[0]; + $expected_first[ $key ] = true; + $expected_second[ $key ] = false; + } + + // Add to memcached. + self::assertEquals( $this->object_cache->add_multiple( $values ), $expected_first ); + + // Check local cache. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $input_array[0], + 'found' => true, + ] ); + } + + // Fails to add all but the new one now because they already exists. + $values['something-new'] = 'test'; + $expected_second['something-new'] = true; + self::assertEquals( $this->object_cache->add_multiple( $values ), $expected_second ); + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_replace( $value, $replace_value ) { + // Add to memcached first. + self::assertTrue( $this->object_cache->add( 'key', $value ) ); + + // Replace with new value. + self::assertTrue( $this->object_cache->replace( 'key', $replace_value ) ); + + // Check local cache. + $cache_key = $this->object_cache->key( 'key', 'default' ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $replace_value, + 'found' => true, + ] ); + + // Can't replace a value that isn't set yet. + self::assertFalse( $this->object_cache->replace( 'new_key', $replace_value ) ); + + // TODO: Test unsetting from local cache after memcached failure. + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_replace_for_non_persistent_groups( $value, $replace_value ) { + $group = 'do-not-persist-me'; + + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Add to memcached first. + self::assertTrue( $this->object_cache->add( 'key', $value, $group ) ); + + // Replace with new value. + self::assertTrue( $this->object_cache->replace( 'key', $replace_value, $group ) ); + + // Check local cache. + $cache_key = $this->object_cache->key( 'key', $group ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $replace_value, + 'found' => false, + ] ); + + // Can't replace a value that isn't set yet. + self::assertFalse( $this->object_cache->replace( 'new_key', $replace_value, $group ) ); + + // Never made it's way to the remote cache. + unset( $this->object_cache->cache[ $cache_key ] ); + $this->object_cache->no_mc_groups = []; + self::assertFalse( $this->object_cache->get( 'key', $group ) ); + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_set( $value, $update_value ) { + $cache_key = $this->object_cache->key( 'key', 'default' ); + + // Set to memcached. + self::assertTrue( $this->object_cache->set( 'key', $value ) ); + + // Check local cache. + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $value, + 'found' => true, + ] ); + + // Update with new value. + self::assertTrue( $this->object_cache->set( 'key', $update_value ) ); + + // Check local cache again. + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $update_value, + 'found' => true, + ] ); + + // TODO: Test local cache result after a failed memcached set + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_set_for_non_persistent_groups( $value ) { + $group = 'do-not-persist-me'; + + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Set in local cache. + self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); + + // Check local cache. + $cache_key = $this->object_cache->key( 'key', $group ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $value, + 'found' => false, + ] ); + + // Never made it's way to the remote cache. + unset( $this->object_cache->cache[ $cache_key ] ); + $this->object_cache->no_mc_groups = []; + self::assertFalse( $this->object_cache->get( 'key', $group ) ); + } + + public function test_set_multiple() { + $inputs = $this->data_cache_inputs(); + + $values = []; + $expected = []; + foreach ( $inputs as $key => $input_array ) { + $values[ $key ] = $input_array[0]; + $expected[ $key ] = true; + } + + // Set to memcached. + self::assertEquals( $this->object_cache->set_multiple( $values ), $expected ); + + // Check local cache. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $input_array[0], + 'found' => true, + ] ); + } + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_get( $value ) { + $cache_key = $this->object_cache->key( 'key', 'default' ); + + // Not found intially. + $found = null; + self::assertFalse( $this->object_cache->get( 'key', 'default', false, $found ) ); + self::assertFalse( $found ); + + // Local cache stored the "not found" state. + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => false, + 'found' => false, + ] ); + + // Will return from local cache if present. + $this->object_cache->cache[ $cache_key ] = [ + 'value' => $value, + 'found' => true, + ]; + $found = null; + self::assertEquals( $this->object_cache->get( 'key', 'default', false, $found ), $value ); + self::assertTrue( $found ); + + // But will skip local cache if forced. + $found = null; + self::assertFalse( $this->object_cache->get( 'key', 'default', true, $found ) ); + self::assertFalse( $found ); + + // Actually set the value remotely now. + self::assertTrue( $this->object_cache->set( 'key', $value ) ); + + $found = null; + self::assertEquals( $this->object_cache->get( 'key', 'default', false, $found ), $value ); + self::assertTrue( $found ); + + // Check that the local cache was saved. + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $value, + 'found' => true, + ] ); + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_get_for_non_persistent_groups( $value ) { + $group = 'do-not-persist-me'; + $cache_key = $this->object_cache->key( 'key', $group ); + + // Before we start, let's put a value in the remote cache then remove from local. + self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); + unset( $this->object_cache->cache[ $cache_key ] ); + + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Fetch from local cache. + $found = null; + self::assertFalse( $this->object_cache->get( 'key', $group, true, $found ) ); + self::assertFalse( $found ); + + // Set in local cache and check again. $found is still false by design. + self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); + $found = null; + self::assertEquals( $this->object_cache->get( 'key', $group, true, $found ), $value ); + self::assertFalse( $found ); + } + + public function test_get_multiple() { + $inputs = $this->data_cache_inputs(); + $keys = array_keys( $inputs ); + + $values = []; + $expected_first = []; + $expected_second = []; + foreach ( $inputs as $key => $input_array ) { + $values[ $key ] = $input_array[0]; + $expected_first[ $key ] = false; + $expected_second[ $key ] = $input_array[0]; + } + + // Each is not found intially. + self::assertEquals( $this->object_cache->get_multiple( $keys ), $expected_first ); + + // Will return from local cache if present. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + + $this->object_cache->cache[ $cache_key ] = [ + 'value' => $input_array[0], + 'found' => true, + ]; + } + self::assertEquals( $this->object_cache->get_multiple( $keys ), $expected_second ); + + // But will skip local cache if forced. + self::assertEquals( $this->object_cache->get_multiple( $keys, 'default', true ), $expected_first ); + + // Set values in remote memcached now, but clear out local cache before fetching. + $this->object_cache->set_multiple( $values ); + $this->object_cache->flush_runtime(); + $fetch_keys = array_merge( $keys, [ 'non-existant-key' ] ); + $expected_second['non-existant-key'] = false; + + self::assertEquals( $this->object_cache->get_multiple( $fetch_keys ), $expected_second ); + + // Ensure local cache was saved. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + self::assertEquals( $this->object_cache->cache[ $cache_key ], [ + 'value' => $input_array[0], + 'found' => true, + ] ); + } + + // As well as saved for the non-existant key. + $non_existant_cache_key = $this->object_cache->key( 'non-existant-key', 'default' ); + self::assertEquals( $this->object_cache->cache[ $non_existant_cache_key ], [ + 'value' => false, + 'found' => false, + ] ); + + // Ensure we still get an array if no keys are found. + self::assertEquals( $this->object_cache->get_multiple( [ 'non-existant-key2', 'non-existant-key3' ] ), [ + 'non-existant-key2' => false, + 'non-existant-key3' => false, + ] ); + + // Test super large multiGets that should be broken up into multiple batches. + $large_mget_values = [ + 'mget_1' => '1', + 'mget_500' => '500', + 'mget_999' => '999', + 'mget_1001' => '1001', + 'mget_1500' => '1500', + 'mget_1999' => '1999', + 'mget_2001' => '2001', + 'mget_2500' => '2500', + 'mget_2999' => '2999', + 'mget_3001' => '3001', + 'mget_3500' => '3500', + 'mget_3999' => '3999', + ]; + $this->object_cache->set_multiple( $large_mget_values ); + $this->object_cache->flush_runtime(); + + $expected_large_mget_values = []; + for ( $i = 0; $i < 4000; $i++ ) { + $key = 'mget_' . $i; + $expected_large_mget_values[ $key ] = isset( $large_mget_values[ $key ] ) ? $large_mget_values[ $key ] : false; + } + + self::assertEquals( $this->object_cache->get_multiple( array_keys( $expected_large_mget_values ) ), $expected_large_mget_values ); + } + + public function test_get_multiple_for_non_persistent_groups() { + $inputs = $this->data_cache_inputs(); + $keys = array_keys( $inputs ); + $group = 'do-not-persist-me'; + + $local_values = []; + $remote_values = []; + $expected_first = []; + $expected_second = []; + foreach ( $inputs as $key => $input_array ) { + $local_values[ $key ] = $input_array[0]; + $remote_values[ $key ] = $input_array[1]; + $expected_first[ $key ] = false; + $expected_second[ $key ] = $input_array[0]; + } + + // Before we start, let's put value in the remote cache then remove from local. + $this->object_cache->set_multiple( $remote_values, $group ); + $this->object_cache->flush_runtime(); + + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Fetch from local cache, should be empty. + self::assertEquals( $this->object_cache->get_multiple( $keys, $group ), $expected_first ); + + // Set in local cache and check again. + $this->object_cache->set_multiple( $local_values, $group ); + self::assertEquals( $this->object_cache->get_multiple( $keys, $group ), $expected_second ); + } + + public function test_get_multi() { + $inputs = $this->data_cache_inputs(); + $keys = array_keys( $inputs ); + + $values = []; + $expected = []; + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + + $values[ $key ] = $input_array[0]; + $expected[ $cache_key ] = $input_array[0]; + } + + $non_existant_cache_key = $this->object_cache->key( 'non-existant-key', 'default' ); + $keys = array_merge( $keys, [ 'non-existant-key' ] ); + $expected[ $non_existant_cache_key ] = false; + + // Populate in memcached but flush local cache after. + $this->object_cache->set_multiple( $values ); + $this->object_cache->flush_runtime(); + + self::assertEquals( $this->object_cache->get_multi( [ 'default' => $keys ] ), $expected ); + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_delete( $value ) { + $cache_key = $this->object_cache->key( 'key', 'default' ); + + // Nothing to delete yet. + self::assertFalse( $this->object_cache->delete( 'key' ) ); + + // Now it can delete. + self::assertTrue( $this->object_cache->set( 'key', $value ) ); + self::assertTrue( $this->object_cache->delete( 'key' ) ); + + // Also removed from local cache. + self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); + } + + /** + * @dataProvider data_cache_inputs + */ + public function test_delete_for_non_persistent_groups( $value ) { + $group = 'do-not-persist-me'; + $cache_key = $this->object_cache->key( 'key', $group ); + + // Set in remote cache first, then add to non-persistent groups. + self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); + $this->object_cache->flush_runtime(); + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Nothing to delete yet. + self::assertFalse( $this->object_cache->delete( 'key', $group ) ); + + // Now it can locally delete. + self::assertTrue( $this->object_cache->set( 'key', $value, $group ) ); + self::assertTrue( $this->object_cache->delete( 'key', $group ) ); + self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); + + // But never made it's way to actually deleting from the remote cache. + $this->object_cache->no_mc_groups = []; + self::assertEquals( $this->object_cache->get( 'key', $group ), $value ); + } + + public function test_delete_multiple() { + $inputs = $this->data_cache_inputs(); + $keys = array_keys( $inputs ); + + $values = []; + $expected_first = []; + $expected_second = []; + foreach ( $inputs as $key => $input_array ) { + $values[ $key ] = $input_array[0]; + $expected_first[ $key ] = false; + $expected_second[ $key ] = true; + } + + // Nothing to delete yet. + self::assertEquals( $this->object_cache->delete_multiple( $keys ), $expected_first ); + + // Now it can delete. + $keys = array_merge( $keys, [ 'non-existant-key' ] ); + $expected_second['non-existant-key'] = false; + $this->object_cache->set_multiple( $values ); + self::assertEquals( $this->object_cache->delete_multiple( $keys ), $expected_second ); + + // Also removed from local cache. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, 'default' ); + self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); + } + } + + public function test_delete_multiple_for_non_persistent_groups() { + $inputs = $this->data_cache_inputs(); + $keys = array_keys( $inputs ); + $group = 'do-not-persist-me'; + + $values = []; + $expected_first = []; + $expected_second = []; + $expected_third = []; + foreach ( $inputs as $key => $input_array ) { + $values[ $key ] = $input_array[0]; + $expected_first[ $key ] = false; + $expected_second[ $key ] = true; + $expected_third[ $key ] = $input_array[0]; + } + + // Set in remote cache first, then add to non-persistent groups. + $this->object_cache->set_multiple( $values, $group ); + $this->object_cache->flush_runtime(); + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Nothing to delete yet. + self::assertEquals( $this->object_cache->delete_multiple( $keys, $group ), $expected_first ); + + // Now it can delete. + $keys = array_merge( $keys, [ 'non-existant-key' ] ); + $expected_second['non-existant-key'] = false; + $this->object_cache->set_multiple( $values, $group ); + self::assertEquals( $this->object_cache->delete_multiple( $keys, $group ), $expected_second ); + + // Also removed from local cache. + foreach ( $inputs as $key => $input_array ) { + $cache_key = $this->object_cache->key( $key, $group ); + self::assertFalse( isset( $this->object_cache->cache[ $cache_key ] ) ); + } + + // But not from remote cache. + $this->object_cache->no_mc_groups = []; + self::assertEquals( $this->object_cache->get_multiple( array_keys( $inputs ), $group ), $expected_third ); + } + + public function test_incr() { + // Increments by 1 by default. + $this->object_cache->add( 'key', 1 ); + self::assertEquals( $this->object_cache->incr( 'key' ), 2 ); + + // Can increment by a specified amount + $this->object_cache->add( 'key2', 1 ); + self::assertEquals( $this->object_cache->incr( 'key2', 5 ), 6 ); + + // Returns false if key doesn't exist yet. + self::assertFalse( $this->object_cache->incr( 'key3' ) ); + + // Memcache extension throws notices for the following tests, memcached does not (despite what the docs say). + if ( ! $this->is_using_memcached_ext ) { + self::expectNotice(); + } + + // Fails if value is non-int. + $this->object_cache->add( 'key4', 'non-numeric' ); + self::assertFalse( $this->object_cache->incr( 'key4' ) ); + + $this->object_cache->add( 'key5', [ 'non-numeric' ] ); + self::assertFalse( $this->object_cache->incr( 'key5' ) ); + + $this->object_cache->add( 'key6', 1.234 ); + self::assertFalse( $this->object_cache->incr( 'key6' ) ); + } + + public function test_incr_for_non_persistent_groups() { + $group = 'do-not-persist-me'; + $cache_key = $this->object_cache->key( 'key', $group ); + + // Set in remote cache first, then add to non-persistent groups. + self::assertTrue( $this->object_cache->set( 'key', 100, $group ) ); + $this->object_cache->flush_runtime(); + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Nothing to increment yet. + self::assertFalse( $this->object_cache->incr( 'key', 1, $group ) ); + + // Now it can locally increment. + self::assertTrue( $this->object_cache->add( 'key', 0, $group ) ); + self::assertEquals( $this->object_cache->incr( 'key', 2, $group ), 2 ); + + // Fails if value is non-int. + $this->object_cache->add( 'key2', 'non-numeric', $group ); + self::assertFalse( $this->object_cache->incr( 'key2', 1, $group ) ); + + $this->object_cache->add( 'key3', [ 'non-numeric' ], $group ); + self::assertFalse( $this->object_cache->incr( 'key3', 1, $group ) ); + + $this->object_cache->add( 'key4', 1.234, $group ); + self::assertFalse( $this->object_cache->incr( 'key4', 1, $group ) ); + + // But the changes never made their way to the remote cache. + $this->object_cache->flush_runtime(); + $this->object_cache->no_mc_groups = []; + self::assertEquals( $this->object_cache->get( 'key', $group ), 100 ); + } + + public function test_decr() { + // Decrement by 1 by default. + $this->object_cache->add( 'key', 1 ); + self::assertEquals( $this->object_cache->decr( 'key' ), 0 ); + + // Can decrement by a specified amount + $this->object_cache->add( 'key2', 5 ); + self::assertEquals( $this->object_cache->decr( 'key2', 2 ), 3 ); + + // Returns false if key doesn't exist yet. + self::assertFalse( $this->object_cache->decr( 'key3' ) ); + + // Returns zero if decremented value would have been less than 0. + $this->object_cache->add( 'key4', 2 ); + self::assertEquals( $this->object_cache->decr( 'key4', 3 ), 0 ); + + // Memcache extension throws notices for the following tests, memcached does not (despite what the docs say). + if ( ! $this->is_using_memcached_ext ) { + self::expectNotice(); + } + + // Fails if value is non-int. + $this->object_cache->add( 'key5', 'non-numeric' ); + self::assertFalse( $this->object_cache->decr( 'key5' ) ); + + $this->object_cache->add( 'key6', [ 'non-numeric' ] ); + self::assertFalse( $this->object_cache->decr( 'key6' ) ); + + $this->object_cache->add( 'key7', 1.234 ); + self::assertFalse( $this->object_cache->decr( 'key7' ) ); + } + + public function test_decr_for_non_persistent_groups() { + $group = 'do-not-persist-me'; + $cache_key = $this->object_cache->key( 'key', $group ); + + // Set in remote cache first, then add to non-persistent groups. + self::assertTrue( $this->object_cache->set( 'key', 100, $group ) ); + $this->object_cache->flush_runtime(); + $this->object_cache->add_non_persistent_groups( [ $group ] ); + + // Nothing to decrement yet. + self::assertFalse( $this->object_cache->decr( 'key', 1, $group ) ); + + // Now it can locally decrement. + self::assertTrue( $this->object_cache->add( 'key', 4, $group ) ); + self::assertEquals( $this->object_cache->decr( 'key', 2, $group ), 2 ); + + // Returns zero if decremented value would have been less than 0. + $this->object_cache->add( 'key2', 2 ); + self::assertEquals( $this->object_cache->decr( 'key2', 3 ), 0 ); + + // Fails if value is non-int. + $this->object_cache->add( 'key3', 'non-numeric', $group ); + self::assertFalse( $this->object_cache->decr( 'key3', 1, $group ) ); + + $this->object_cache->add( 'key4', [ 'non-numeric' ], $group ); + self::assertFalse( $this->object_cache->decr( 'key4', 1, $group ) ); + + $this->object_cache->add( 'key5', 1.234, $group ); + self::assertFalse( $this->object_cache->decr( 'key5', 1, $group ) ); + + // But the changes never made their way to the remote cache. + $this->object_cache->flush_runtime(); + $this->object_cache->no_mc_groups = []; + self::assertEquals( $this->object_cache->get( 'key', $group ), 100 ); + } + + public function test_flush() { + // Add a value to memcached (local and remote) + self::assertTrue( $this->object_cache->add( 'key', 'data' ) ); + $cache_key = $this->object_cache->key( 'key', 'default' ); + self::assertNotEmpty( $this->object_cache->cache[ $cache_key ] ); + + $initial_site_flush_number = $this->object_cache->flush_number[ $this->object_cache->blog_prefix ]; + $initial_global_flush_number = $this->object_cache->global_flush_number; + self::assertTrue( $this->object_cache->flush() ); + $new_site_flush_number = $this->object_cache->flush_number[ $this->object_cache->blog_prefix ]; + $new_global_flush_number = $this->object_cache->global_flush_number; + + // Ensure the local cache was flushed. + // Note: Local cache won't be completely empty because it stores updated flush number in local cache. + self::assertArrayNotHasKey( $cache_key, $this->object_cache->cache ); + + // Ensure it can't pull from remote cache either. + self::assertFalse( $this->object_cache->get( 'key' ) ); + + // Ensure the flush keys were rotated. + self::assertNotEmpty( $new_site_flush_number ); + self::assertNotEmpty( $new_global_flush_number ); + self::assertNotEquals( $initial_site_flush_number, $new_site_flush_number ); + self::assertNotEquals( $initial_global_flush_number, $new_global_flush_number ); + } + + public function test_flush_runtime() { + // Add a value to memcached (local and remote) + self::assertTrue( $this->object_cache->add( 'key', 'data' ) ); + $cache_key = $this->object_cache->key( 'key', 'default' ); + self::assertNotEmpty( $this->object_cache->cache[ $cache_key ] ); + self::assertNotEmpty( $this->object_cache->group_ops ); + + self::assertTrue( $this->object_cache->flush_runtime() ); + + // Gone from local cache. + self::assertArrayNotHasKey( $cache_key, $this->object_cache->cache ); + self::assertEquals( $this->object_cache->group_ops, [] ); + + // But exists remotely still + self::assertEquals( $this->object_cache->get( 'key' ), 'data' ); + } + + public function test_add_global_groups() { + // Starts out with one element in array already. + self::assertEquals( $this->object_cache->global_groups, [ $this->object_cache->global_flush_group ] ); + + // Accepts a single string. + $single_group = 'single-group'; + $this->object_cache->add_global_groups( $single_group ); + $this->assertContains( $single_group, $this->object_cache->global_groups ); + + // Or an array (but removes duplicates). + $duplicate_groups = [ $single_group, 'another-group', 'another-group' ]; + $this->object_cache->add_global_groups( $duplicate_groups ); + $this->assertContains( $single_group, $this->object_cache->global_groups ); + $this->assertContains( 'another-group', $this->object_cache->global_groups ); + $this->assertEquals( count( $this->object_cache->global_groups ), 3 ); + } + + public function test_add_non_persistent_groups() { + // Starts out as an empty array. + self::assertEmpty( $this->object_cache->no_mc_groups ); + + // Accepts a single string. + $single_group = 'single-group'; + $this->object_cache->add_non_persistent_groups( $single_group ); + $this->assertContains( $single_group, $this->object_cache->no_mc_groups ); + + // Or an array (but removes duplicates). + $duplicate_groups = [ $single_group, 'another-group', 'another-group' ]; + $this->object_cache->add_non_persistent_groups( $duplicate_groups ); + $this->assertContains( $single_group, $this->object_cache->no_mc_groups ); + $this->assertContains( 'another-group', $this->object_cache->no_mc_groups ); + $this->assertEquals( count( $this->object_cache->no_mc_groups ), 2 ); + } + + public function test_switch_to_blog() { + global $table_prefix; + + $initial_blog_prefix = $this->object_cache->blog_prefix; + $this->object_cache->switch_to_blog( 2 ); + + if ( is_multisite() ) { + self::assertEquals( $this->object_cache->blog_prefix, 2 ); + } else { + self::assertEquals( $this->object_cache->blog_prefix, $table_prefix ); + } + } + + public function test_close() { + self::assertTrue( $this->object_cache->close() ); + + // TODO: Further testing requires being able to be able to inject/mock the adapters. + } + + /* + |-------------------------------------------------------------------------- + | Various edge cases. + |-------------------------------------------------------------------------- + */ + + public function test_large_key_lengths() { + $large_keys = [ + str_repeat( 'a', 100 ) => 'a value', + str_repeat( 'b', 250 ) => 'b value', + str_repeat( 'c', 1000 ) => 'c value', + ]; + + foreach ( $large_keys as $key => $_value ) { + self::assertTrue( $this->object_cache->add( $key, 1 ) ); + self::assertTrue( $this->object_cache->replace( $key, 2 ) ); + self::assertTrue( $this->object_cache->set( $key, 3 ) ); + self::assertEquals( $this->object_cache->incr( $key ), 4 ); + self::assertEquals( $this->object_cache->decr( $key, 3 ), 1 ); + self::assertEquals( $this->object_cache->get( $key ), 1 ); + self::assertTrue( $this->object_cache->delete( $key ) ); + } + + self::assertSame( $this->object_cache->add_multiple( $large_keys ), array_map( fn() => true, $large_keys ) ); + self::assertSame( $this->object_cache->delete_multiple( array_keys( $large_keys ) ), array_map( fn() => true, $large_keys ) ); + self::assertSame( $this->object_cache->set_multiple( $large_keys ), array_map( fn() => true, $large_keys ) ); + self::assertSame( $this->object_cache->get_multiple( array_keys( $large_keys ) ), $large_keys ); + } + + /* + |-------------------------------------------------------------------------- + | Internal methods, mostly deals with flush numbers, the pseudo-cache-flushing mechanic. + |-------------------------------------------------------------------------- + */ + + public function test_flush_prefix() { + // Does not flush the flush groups. + self::assertEquals( '_:', $this->object_cache->flush_prefix( $this->object_cache->flush_group ) ); + self::assertEquals( '_:', $this->object_cache->flush_prefix( $this->object_cache->global_flush_group ) ); + + // TODO: sets global vs sets blog prefix. + // test_flush_prefix_sets_global_flush_number_for_global_groups + // test_flush_prefix_sets_flush_number_for_non_global_groups + } + + public function test_key() { + // Uses "default" group by default. + self::assertStringContainsString( 'default:foo', $this->object_cache->key( 'foo', '' ) ); + + // Contains global prefix for global groups. + $this->object_cache->add_global_groups( [ 'global-group' ] ); + $this->object_cache->global_prefix = 'global_prefix'; // Mock for non-multisite tests. + self::assertStringContainsString( $this->object_cache->global_prefix, $this->object_cache->key( 'foo', 'global-group' ) ); + + // Contains blog prefix for non-global groups. + $this->object_cache->blog_prefix = 'blog_prefix'; // Mock for non-multisite tests. + $this->assertStringContainsString( $this->object_cache->blog_prefix, $this->object_cache->key( 'foo', 'non-global-group' ) ); + } + + public function test_non_persistent_themes_group() { + $key = 'theme-test-key'; + $group = 'themes'; + $data = [ + 'block_theme' => true, + 'block_template_folders' => [ + 'wp_template' => 'templates', + 'wp_template_part' => 'parts' + ], + 'headers' => [ + 'Name' => 'Test Theme', + ], + 'stylesheet' => 'test-theme', + 'template' => 'test-theme' + ]; + $expiration = 300; + + $this->object_cache->add_non_persistent_groups( 'themes' ); + + // Ensure 'themes' is in non-persistent groups + $this->assertContains( $group, $this->object_cache->no_mc_groups, "'themes' should be in non-persistent groups" ); + + // Step 1: Attempt to get the data before adding + $pre_get_result = $this->object_cache->get( $key, $group ); + $this->assertFalse( $pre_get_result, 'Data should not be present before adding to non-persistent group' ); + + // Step 2: Attempt to add the data to cache + $add_result = $this->object_cache->add( $key, $data, $group, $expiration ); + $this->assertTrue( $add_result, 'Adding data to non-persistent group should succeed' ); + + // Step 3: Attempt to get the data immediately after adding + $get_result = $this->object_cache->get( $key, $group ); + $this->assertEquals( $data, $get_result, 'Data should be retrieved immediately after adding to non-persistent group' ); + } + + public function test_non_persistent_group_behavior() { + $group = 'non_persistent_group'; + $this->object_cache->add_non_persistent_groups( $group ); + + // Test 1: Verify that get( ) returns false for a non-existent key + $this->assertFalse( $this->object_cache->get( 'non_existent_key', $group ) ); + + // Test 2: Test setting and getting a value + $this->assertTrue( $this->object_cache->set( 'test_key', 'test_value', $group ) ); + $this->assertEquals( 'test_value', $this->object_cache->get( 'test_key', $group ) ); + + // Test 3: Verify that add( ) succeeds for a new key + $this->assertTrue( $this->object_cache->add( 'new_key', 'new_value', $group ) ); + $this->assertEquals( 'new_value', $this->object_cache->get( 'new_key', $group ) ); + + // Test 4: Verify that add( ) fails for an existing key + $this->assertFalse( $this->object_cache->add( 'test_key', 'another_value', $group ) ); + $this->assertEquals( 'test_value', $this->object_cache->get( 'test_key', $group ) ); + + // Test 5: Test that replace( ) works for an existing key + $this->assertTrue( $this->object_cache->replace( 'test_key', 'replaced_value', $group ) ); + $this->assertEquals( 'replaced_value', $this->object_cache->get( 'test_key', $group ) ); + + // Test 6: Verify that replace( ) fails for a non-existent key + $this->assertFalse( $this->object_cache->replace( 'non_existent_key', 'some_value', $group ) ); + + // Test 7: Test deleting a key + $this->assertTrue( $this->object_cache->delete( 'test_key', $group ) ); + $this->assertFalse( $this->object_cache->get( 'test_key', $group ) ); + + // Test 8: Verify that delete returns false for non-existent key + $this->assertFalse( $this->object_cache->delete( 'non_existent_key', $group ) ); + } + + public function test_non_persistent_group_multiple_operations() { + $group = 'non_persistent_group'; + $this->object_cache->add_non_persistent_groups( $group ); + + // Test 1: Verify get_multiple behavior + $keys = ['key1', 'key2', 'key3']; + $results = $this->object_cache->get_multiple( $keys, $group ); + foreach ( $keys as $key ) { + $this->assertFalse( $results[$key] ); + } + + // Test 2: Verify an add() and a following get() still work + $add_result = $this->object_cache->add( 'key1', 'value', $group ); + $get_result = $this->object_cache->get( 'key1', $group ); + $this->assertEquals( 'value', $get_result, 'Data should be retrieved immediately after adding to non-persistent group' ); + + // Test 3: Verify set_multiple and get_multiple + $data = ['key1' => 'value1', 'key2' => 'value2']; + $this->assertEquals( $data, $this->object_cache->set_multiple( $data, $group ) ); + $results = $this->object_cache->get_multiple( array_keys( $data ), $group ); + $this->assertEquals( $data, $results ); + + // Test 4: Verify delete_multiple + $this->assertEquals( + ['key1' => true, 'key2' => true], + $this->object_cache->delete_multiple( array_keys( $data ), $group ) + ); + $results = $this->object_cache->get_multiple( array_keys( $data ), $group ); + foreach ( array_keys( $data ) as $key ) { + $this->assertFalse( $results[$key] ); + } + } + + public function test_non_persistent_group_increment_decrement() { + $group = 'non_persistent_group'; + $this->object_cache->add_non_persistent_groups( $group ); + + // Test 1: Increment a non-existent key + $this->assertFalse( $this->object_cache->incr( 'counter', 1, $group ) ); + + // Test 2: Set and increment a key + $this->object_cache->set( 'counter', 5, $group ); + $this->assertEquals( 6, $this->object_cache->incr( 'counter', 1, $group ) ); + + // Test 3: Decrement the key + $this->assertEquals( 5, $this->object_cache->decr( 'counter', 1, $group ) ); + + // Test 4: Decrement below zero + $this->assertEquals( 0, $this->object_cache->decr( 'counter', 10, $group ) ); + + // Test 5: Increment and decrement with larger values + $this->object_cache->set( 'counter', 50, $group ); + $this->assertEquals( 100, $this->object_cache->incr( 'counter', 50, $group ) ); + $this->assertEquals( 75, $this->object_cache->decr( 'counter', 25, $group ) ); + } + + public function test_non_persistent_group_flush_behavior() { + $group = 'non_persistent_group'; + $this->object_cache->add_non_persistent_groups( $group ); + + // Set up some data + $this->object_cache->set( 'key1', 'value1', $group ); + $this->object_cache->set( 'key2', 'value2', $group ); + + // Test 1: Verify that flush() clears non-persistent group data + $this->assertTrue( $this->object_cache->flush() ); + $this->assertFalse( $this->object_cache->get( 'key1', $group ) ); + $this->assertFalse( $this->object_cache->get( 'key2', $group ) ); + + // Set up data again + $this->object_cache->set( 'key1', 'value1', $group ); + $this->object_cache->set( 'key2', 'value2', $group ); + + // Test 2: Verify that flush_runtime( ) clears non-persistent group data + $this->assertTrue( $this->object_cache->flush_runtime() ); + $this->assertFalse( $this->object_cache->get( 'key1', $group ) ); + $this->assertFalse( $this->object_cache->get( 'key2', $group ) ); + } + + public function test_non_persistent_group_replace() { + $group = 'non_persistent_group'; + $this->object_cache->add_non_persistent_groups( $group ); + + // Test 1: Replace should fail for a non-existent key + $this->assertFalse( $this->object_cache->replace( 'non_existent_key', 'some_value', $group ) ); + + // Test 2: Set a value, then replace it + $this->assertTrue( $this->object_cache->set( 'test_key', 'initial_value', $group ) ); + $this->assertTrue( $this->object_cache->replace( 'test_key', 'replaced_value', $group ) ); + $this->assertEquals( 'replaced_value', $this->object_cache->get( 'test_key', $group ) ); + + // Test 3: Attempt to replace after a failed get + $this->assertFalse( $this->object_cache->get( 'another_key', $group ) ); + $this->assertFalse( $this->object_cache->replace( 'another_key', 'new_value', $group ) ); + + // Test 4: Set, delete, then attempt to replace + $this->object_cache->set( 'delete_me', 'delete_value', $group ); + $this->object_cache->delete( 'delete_me', $group ); + $this->assertFalse( $this->object_cache->replace( 'delete_me', 'after_delete_value', $group ) ); + } + + /* + |-------------------------------------------------------------------------- + | Testing Utils + |-------------------------------------------------------------------------- + */ + + public function data_cache_inputs() { + $object = new stdClass(); + $object->property = 'test'; + + // Key => [ first value, updated value ] + return [ + 'empty-string' => [ '', 'updated' ], + 'empty-array' => [ [], [ 'updated' ] ], + 'empty-object' => [ new stdClass(), $object ], + 'zero' => [ 0, 1 ], + 'one' => [ 1, 0 ], + 'false' => [ false, true ], + 'true' => [ true, false ], + 'null' => [ null, 'notnull' ], + 'basic-array' => [ [ 'basic', 'array' ], [] ], + 'complex-array' => [ + [ + 'a' => 'multi', + 'dimensional' => [ 'array', 'example' ], + ], + [], + ], + 'string' => [ 'string', '' ], + 'float' => [ 1.234, 5.678 ], + 'object' => [ $object, new stdClass() ], + ]; + } +} From 3d0e2794d38eff63f598fb598bc032115c59ee33 Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Thu, 21 Nov 2024 16:39:46 -0600 Subject: [PATCH 3/4] put flush key back to v5 --- drop-ins/wp-memcached/includes/wp-object-cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drop-ins/wp-memcached/includes/wp-object-cache.php b/drop-ins/wp-memcached/includes/wp-object-cache.php index 0bc14dec38..4a51c66a88 100644 --- a/drop-ins/wp-memcached/includes/wp-object-cache.php +++ b/drop-ins/wp-memcached/includes/wp-object-cache.php @@ -10,7 +10,7 @@ class WP_Object_Cache { public string $flush_group = 'WP_Object_Cache'; public string $global_flush_group = 'WP_Object_Cache_global'; - public string $flush_key = 'flush_number_v4'; + public string $flush_key = 'flush_number_v5'; /** * Keep track of flush numbers. From c0630212f033e0f650aeec679fb6a802c7040110 Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Thu, 21 Nov 2024 16:44:08 -0600 Subject: [PATCH 4/4] replace alloptions hotfix for < wp 6.2 --- .../wp-memcached/includes/wp-object-cache.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drop-ins/wp-memcached/includes/wp-object-cache.php b/drop-ins/wp-memcached/includes/wp-object-cache.php index 4a51c66a88..90273827d1 100644 --- a/drop-ins/wp-memcached/includes/wp-object-cache.php +++ b/drop-ins/wp-memcached/includes/wp-object-cache.php @@ -132,7 +132,8 @@ public function __construct( $adapter = null ) { * @return bool True on success, false on failure or if cache key and group already exist. */ public function add( $key, $data, $group = 'default', $expire = 0 ) { - $key = $this->key( $key, $group ); + $is_alloptions = 'alloptions' === $key && 'options' === $group; + $key = $this->key( $key, $group ); if ( is_object( $data ) ) { $data = clone $data; @@ -173,6 +174,21 @@ public function add( $key, $data, $group = 'default', $expire = 0 ) { $this->group_ops_stats( 'add', $key, $group, $size, $elapsed, $comment ); + // Special handling for alloptions on WP < 6.2 (before pre_wp_load_alloptions filter). + // A) If the add() fails, + if ( false === $result && $is_alloptions && version_compare( $GLOBALS['wp_version'], '6.2', '<' ) ) { + // B) And there is still nothing retrieved with a remote get(), + if ( false === $this->get( 'alloptions', 'options', true ) ) { + // C) Then we'll keep the fresh value in the runtime cache to help keep performance stable. + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => false, + ]; + } + + return $result; + } + if ( $result ) { $this->cache[ $key ] = [ 'value' => $data,