diff --git a/.github/workflows/tests-php.yml b/.github/workflows/tests-php.yml index afb88142..845a296a 100644 --- a/.github/workflows/tests-php.yml +++ b/.github/workflows/tests-php.yml @@ -6,7 +6,8 @@ jobs: strategy: matrix: suite: - - wpunit + - wpunit --env singlesite + - wpunit --env multisite - muwpunit runs-on: ubuntu-latest steps: @@ -69,6 +70,6 @@ jobs: - name: Set up StellarWP Uplink run: | ${SLIC_BIN} use uplink - ${SLIC_BIN} composer install --ignore-platform-reqs + ${SLIC_BIN} composer install - name: Run suite tests run: ${SLIC_BIN} run ${{ matrix.suite }} --ext DotReporter diff --git a/.gitignore b/.gitignore index 48e74aad..70adfcd2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ files/ repo/ vendor/ .idea/ +/composer.lock diff --git a/README.md b/README.md index 0408fcc2..1ca21c87 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,15 @@ add_action( 'plugins_loaded', function() { Config::set_container( $container ); Config::set_hook_prefix( 'my-custom-prefix' ); + /* + * If you wish to allow a customer to authorize their product, set your Token Auth Prefix. + * + * This will allow storage of a unique token associated with the customer's license/domain. + * + * Important: The Token auth prefix should be the same across all of your products. + */ + Config::set_token_auth_prefix( 'my_origin' ); + Uplink::init(); }, 0 ); ``` @@ -44,11 +53,11 @@ add_action( 'plugins_loaded', function() { Package is using `__( 'Invalid request: nonce field is expired. Please try again.', '%TEXTDOMAIN%' )` function for translation. In order to change domain placeholder `'%TEXTDOMAIN%'` to your plugin translation domain run ```bash -./vendor/bin/stellar-uplink domain= +./bin/stellar-uplink domain= ``` or ```bash -./vendor/bin/stellar-uplink +./bin/stellar-uplink ``` and prompt the plugin domain You can also add lines below to your composer file in order to run command automatically @@ -116,11 +125,11 @@ Registers a service for licensing. Since services require a plugin, we pull vers ```php use StellarWP\Uplink\Register; -$service_slug = 'my-service'; -$service_name = 'My Service'; -$plugin_version = MyPlugin::VERSION; -$plugin_path = 'my-plugin/my-plugin.php'; -$plugin_class = MyPlugin::class; +$service_slug = 'my-service'; +$service_name = 'My Service'; +$service_version = MyPlugin::VERSION; +$plugin_path = 'my-plugin/my-plugin.php'; +$plugin_class = MyPlugin::class; Register::service( $service_slug, @@ -174,3 +183,98 @@ function render_settings_page() { //.... } ``` + +## License Authorization + +> ⚠️ Your `auth_url` is set on the Origins table on the [Stellar Licensing](https://github.com/stellarwp/licensing) server! +> You must first request to have this added before proceeding. + +There may be certain functionality you wish to make available when you know a license is authorized. + +This library provides the tools to fetch and store unique tokens, working together with the Uplink Origin +plugin. + +After following the instructions at the top to define a `Config::set_token_auth_prefix()`, this will enable the following +functionality: + +1. The ability to render a "Connect" button anywhere in your plugin while the user is in wp-admin, using the provided function below. +2. The button will display "Disconnect" once they are authorized, which deletes the locally stored Token. +3. The ability for the customer's site to accept specific Query Variables in wp-admin, that will store the generated Token, and an optional new License Key for a Product Slug. +4. Check if a license is authorized, either in the License Validation payload, or manually. + +> ⚠️ Generating a Token requires manual configuration on your Origin site using the [Uplink Origin Plugin](https://github.com/stellarwp/uplink-origin). + +### Render Authorize Button + +> 💡 Note: the button is only rendered if the following conditions are met: + +1. You have an `auth_url` set on the StellarWP Licensing Server. +2. The current user is a Super Admin (can be changed with a WP filter). +3. This is not a multisite installation, or... +4. If multisite and using subfolders, only on the root network dashboard. +5. If multisite and NOT using subfolders and on a subsite AND a token doesn't already exist at the network level, in which case it needs to be managed at the network. + +```php +// Call the namespaced function with your plugin slug. +\StellarWP\Uplink\render_authorize_button( 'kadence-blocks-pro' ); +``` + +> 💡 The button is very customizable with filters, see [Authorize_Button_Controller.php](src/Uplink/Components/Admin/Authorize_Button_Controller.php). + +### Manually Check if a License is Remotely Authorized + +This connects to the licensing server to check in real time if the license is authorized. Use sparingly. + +```php +$container = \StellarWP\Uplink\Config::get_container(); +$token_manager = $container->get( \StellarWP\Uplink\Auth\Token\Contracts\Token_Manager::class ); +$token = $token_manager->get(); + +if ( ! $token ) { + return; +} + +$is_authorized = \StellarWP\Uplink\is_authorized( 'customer_license_key', $token, 'customer_domain' ); + +echo $is_authorized ? esc_html__( 'authorized' ) : esc_html__( 'not authorized' ); +``` + +### Manually Fetch Auth URL + +If for some reason you need to fetch your `auth_url` manually, you can do so by: + +```php +$container = \StellarWP\Uplink\Config::get_container(); +$auth_url_manager = $container->get( \StellarWP\Uplink\API\V3\Auth\Contracts\Auth_Url::class ); + +// Pass your product or service slug. +$auth_url = $auth_url_manager->get( 'kadence-blocks-pro' ); + +echo $auth_url; +``` + +> 💡 Auth URL connections are cached for one day using transients. + + +### Callback Redirect + +The Callback Redirect generated by the Origin looks something like this, where `uplinksample.lndo.site` is your +customer's website: + +``` +https://uplinksample.lndo.site/wp-admin/import.php?uplink_token=d9a407d0-0eb1-41cf-8cd0-e5da668143b4&_uplink_nonce=Oyj13TCvhaa12IJm +``` + +The Origin is responsible for asking StellarWP Licensing to generate a token and redirect back to where the customer originally +clicked on the button. + +The following Query Variables are available for reference: + +> 💡 Note: This data automatically gets stored when detected, using the `admin_init` hook! + +1. `uplink_token` - The unique UUIDv4 token generated by StellarWP Licensing. +2. `_uplink_nonce` - The original nonce sent with the callback URL, as part of the "Connect" button. +3. `uplink_license` (optional) - Whether we should also update or set a License Key. +4. `uplink_slug` (optional) - The Product or Service Slug that we're updating the license for. + +> ⚠️ `uplink_slug` MUST be supplied if `uplink_license` is! diff --git a/composer.json b/composer.json index 495ac8bd..38dcfa4f 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,8 @@ "homepage": "https://stellarwp.com" } ], + "minimum-stability": "dev", + "prefer-stable": true, "bin": ["bin/stellar-uplink"], "autoload": { "psr-4": { @@ -19,11 +21,15 @@ }, "autoload-dev": { "psr-4": { - "StellarWP\\Uplink\\Tests\\": "tests/_support/Helper/" + "StellarWP\\Uplink\\Tests\\": [ + "tests/wpunit", + "tests/_support/Helper/" + ] } }, "require": { "php": ">=7.1", + "ext-json": "*", "stellarwp/container-contract": "^1.0" }, "config": { @@ -44,14 +50,16 @@ "codeception/util-universalframework": "^1.0", "lucatume/di52": "^3.0", "lucatume/wp-browser": "^3.0.14", - "phpunit/phpunit": "~6.0", + "phpspec/prophecy": "^1.0", + "phpspec/prophecy-phpunit": "^1.0|^2.0", + "phpunit/phpunit": "^6.0|^7.0|^8.0|^9.0", "symfony/event-dispatcher-contracts": "^2.5.1", "symfony/string": "^5.4", "szepeviktor/phpstan-wordpress": "^1.1" }, "scripts": { "test:analysis": [ - "phpstan analyse -c phpstan.neon.dist --memory-limit=512M" + "phpstan analyse -c phpstan.neon.dist --memory-limit=-1" ] }, "scripts-descriptions": { diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 2b5409ad..00000000 --- a/composer.lock +++ /dev/null @@ -1,6414 +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": "917a597e6c857c063edc3f9ae055d7d1", - "packages": [ - { - "name": "stellarwp/container-contract", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/stellarwp/container-contract.git", - "reference": "37becc9edbecb0ff95556048337600dd9cc888f0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/stellarwp/container-contract/zipball/37becc9edbecb0ff95556048337600dd9cc888f0", - "reference": "37becc9edbecb0ff95556048337600dd9cc888f0", - "shasum": "" - }, - "require": { - "php": ">=7.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "StellarWP\\ContainerContract\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0-or-later" - ], - "authors": [ - { - "name": "StellarWP", - "homepage": "https://stellarwp.com" - } - ], - "description": "StellarWP Container Interface", - "homepage": "https://github.com/stellarwp/container-contract", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop" - ], - "support": { - "issues": "https://github.com/stellarwp/container-contract/issues", - "source": "https://github.com/stellarwp/container-contract/tree/1.0.4" - }, - "time": "2022-12-20T21:29:17+00:00" - } - ], - "packages-dev": [ - { - "name": "antecedent/patchwork", - "version": "2.1.21", - "source": { - "type": "git", - "url": "https://github.com/antecedent/patchwork.git", - "reference": "25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d", - "reference": "25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": ">=4" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignas Rudaitis", - "email": "ignas.rudaitis@gmail.com" - } - ], - "description": "Method redefinition (monkey-patching) functionality for PHP.", - "homepage": "http://patchwork2.org/", - "keywords": [ - "aop", - "aspect", - "interception", - "monkeypatching", - "redefinition", - "runkit", - "testing" - ], - "support": { - "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.1.21" - }, - "time": "2022-02-07T07:28:34+00:00" - }, - { - "name": "behat/gherkin", - "version": "v4.9.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", - "shasum": "" - }, - "require": { - "php": "~7.2|~8.0" - }, - "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", - "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ], - "support": { - "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" - }, - "time": "2021-10-12T13:05:09+00:00" - }, - { - "name": "bordoni/phpass", - "version": "0.3.6", - "source": { - "type": "git", - "url": "https://github.com/bordoni/phpass.git", - "reference": "12f8f5cc03ebb7efd69554f104afe9aa1aa46e1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bordoni/phpass/zipball/12f8f5cc03ebb7efd69554f104afe9aa1aa46e1a", - "reference": "12f8f5cc03ebb7efd69554f104afe9aa1aa46e1a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "replace": { - "hautelook/phpass": "0.3.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "Hautelook": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Public Domain" - ], - "authors": [ - { - "name": "Solar Designer", - "email": "solar@openwall.com", - "homepage": "http://openwall.com/phpass/" - }, - { - "name": "Gustavo Bordoni", - "email": "gustavo@bordoni.me", - "homepage": "https://bordoni.me" - } - ], - "description": "Portable PHP password hashing framework", - "homepage": "http://github.com/bordoni/phpass/", - "keywords": [ - "blowfish", - "crypt", - "password", - "security" - ], - "support": { - "issues": "https://github.com/bordoni/phpass/issues", - "source": "https://github.com/bordoni/phpass/tree/0.3.6" - }, - "time": "2012-08-31T00:00:00+00:00" - }, - { - "name": "codeception/codeception", - "version": "4.2.2", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/b88014f3348c93f3df99dc6d0967b0dbfa804474", - "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474", - "shasum": "" - }, - "require": { - "behat/gherkin": "^4.4.0", - "codeception/lib-asserts": "^1.0 | 2.0.*@dev", - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", - "codeception/stub": "^2.0 | ^3.0 | ^4.0", - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "guzzlehttp/psr7": "^1.4 | ^2.0", - "php": ">=5.6.0 <9.0", - "symfony/console": ">=2.7 <6.0", - "symfony/css-selector": ">=2.7 <6.0", - "symfony/event-dispatcher": ">=2.7 <6.0", - "symfony/finder": ">=2.7 <6.0", - "symfony/yaml": ">=2.7 <6.0" - }, - "require-dev": { - "codeception/module-asserts": "^1.0 | 2.0.*@dev", - "codeception/module-cli": "^1.0 | 2.0.*@dev", - "codeception/module-db": "^1.0 | 2.0.*@dev", - "codeception/module-filesystem": "^1.0 | 2.0.*@dev", - "codeception/module-phpbrowser": "^1.0 | 2.0.*@dev", - "codeception/specify": "~0.3", - "codeception/util-universalframework": "*@dev", - "monolog/monolog": "~1.8", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <6.0", - "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" - }, - "suggest": { - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "hoa/console": "For interactive console functionality", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" - }, - "bin": [ - "codecept" - ], - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Codeception\\": "src/Codeception", - "Codeception\\Extension\\": "ext" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "https://codegyre.com" - } - ], - "description": "BDD-style testing framework", - "homepage": "https://codeception.com/", - "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" - ], - "support": { - "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.2.2" - }, - "funding": [ - { - "url": "https://opencollective.com/codeception", - "type": "open_collective" - } - ], - "time": "2022-08-13T13:28:25+00:00" - }, - { - "name": "codeception/lib-asserts", - "version": "1.13.2", - "source": { - "type": "git", - "url": "https://github.com/Codeception/lib-asserts.git", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", - "shasum": "" - }, - "require": { - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", - "ext-dom": "*", - "php": ">=5.6.0 <9.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" - } - ], - "description": "Assertion methods used by Codeception core and Asserts module", - "homepage": "https://codeception.com/", - "keywords": [ - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/lib-asserts/issues", - "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" - }, - "time": "2020-10-21T16:26:20+00:00" - }, - { - "name": "codeception/lib-innerbrowser", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/Codeception/lib-innerbrowser.git", - "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", - "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", - "shasum": "" - }, - "require": { - "codeception/codeception": "4.*@dev", - "ext-dom": "*", - "ext-json": "*", - "ext-mbstring": "*", - "php": ">=5.6.0 <9.0", - "symfony/browser-kit": ">=2.7 <6.0", - "symfony/dom-crawler": ">=2.7 <6.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "require-dev": { - "codeception/util-universalframework": "dev-master" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - }, - { - "name": "Gintautas Miselis" - } - ], - "description": "Parent library for all Codeception framework modules and PhpBrowser", - "homepage": "https://codeception.com/", - "keywords": [ - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/lib-innerbrowser/issues", - "source": "https://github.com/Codeception/lib-innerbrowser/tree/1.5.1" - }, - "time": "2021-08-30T15:21:42+00:00" - }, - { - "name": "codeception/module-asserts", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-asserts.git", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", - "shasum": "" - }, - "require": { - "codeception/codeception": "*@dev", - "codeception/lib-asserts": "^1.13.1", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" - } - ], - "description": "Codeception module containing various assertions", - "homepage": "https://codeception.com/", - "keywords": [ - "assertions", - "asserts", - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/module-asserts/issues", - "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" - }, - "time": "2020-10-21T16:48:15+00:00" - }, - { - "name": "codeception/module-cli", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-cli.git", - "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-cli/zipball/1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f", - "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f", - "shasum": "" - }, - "require": { - "codeception/codeception": "*@dev", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - } - ], - "description": "Codeception module for testing basic shell commands and shell output", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/module-cli/issues", - "source": "https://github.com/Codeception/module-cli/tree/1.1.1" - }, - "time": "2020-12-26T16:56:19+00:00" - }, - { - "name": "codeception/module-db", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-db.git", - "reference": "04c3e66fbd3a3ced17fcccc49627f6393a97b04b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-db/zipball/04c3e66fbd3a3ced17fcccc49627f6393a97b04b", - "reference": "04c3e66fbd3a3ced17fcccc49627f6393a97b04b", - "shasum": "" - }, - "require": { - "codeception/codeception": "*@dev", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - } - ], - "description": "DB module for Codeception", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception", - "database-testing", - "db-testing" - ], - "support": { - "issues": "https://github.com/Codeception/module-db/issues", - "source": "https://github.com/Codeception/module-db/tree/1.2.0" - }, - "time": "2022-03-05T19:38:40+00:00" - }, - { - "name": "codeception/module-filesystem", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-filesystem.git", - "reference": "781be167fb1557bfc9b61e0a4eac60a32c534ec1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-filesystem/zipball/781be167fb1557bfc9b61e0a4eac60a32c534ec1", - "reference": "781be167fb1557bfc9b61e0a4eac60a32c534ec1", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.0", - "php": ">=5.6.0 <9.0", - "symfony/finder": ">=2.7 <6.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - } - ], - "description": "Codeception module for testing local filesystem", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception", - "filesystem" - ], - "support": { - "issues": "https://github.com/Codeception/module-filesystem/issues", - "source": "https://github.com/Codeception/module-filesystem/tree/1.0.3" - }, - "time": "2020-10-24T14:46:40+00:00" - }, - { - "name": "codeception/module-phpbrowser", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-phpbrowser.git", - "reference": "8ba6bede11d0914e74d98691f427fd8f397f192e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-phpbrowser/zipball/8ba6bede11d0914e74d98691f427fd8f397f192e", - "reference": "8ba6bede11d0914e74d98691f427fd8f397f192e", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.1", - "codeception/lib-innerbrowser": "^1.3", - "guzzlehttp/guzzle": "^6.3|^7.0", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "require-dev": { - "codeception/module-rest": "^1.0" - }, - "suggest": { - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - } - ], - "description": "Codeception module for testing web application over HTTP", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception", - "functional-testing", - "http" - ], - "support": { - "issues": "https://github.com/Codeception/module-phpbrowser/issues", - "source": "https://github.com/Codeception/module-phpbrowser/tree/1.0.3" - }, - "time": "2022-05-21T13:50:41+00:00" - }, - { - "name": "codeception/module-rest", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-rest.git", - "reference": "9cd7a87fd9343494e7782f7bdb51687c25046917" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-rest/zipball/9cd7a87fd9343494e7782f7bdb51687c25046917", - "reference": "9cd7a87fd9343494e7782f7bdb51687c25046917", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.0", - "justinrainbow/json-schema": "~5.2.9", - "php": ">=5.6.6 <9.0", - "softcreatr/jsonpath": "^0.5 || ^0.7" - }, - "require-dev": { - "codeception/lib-innerbrowser": "^1.0", - "codeception/util-universalframework": "^1.0" - }, - "suggest": { - "aws/aws-sdk-php": "For using AWS Auth" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gintautas Miselis" - } - ], - "description": "REST module for Codeception", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception", - "rest" - ], - "support": { - "issues": "https://github.com/Codeception/module-rest/issues", - "source": "https://github.com/Codeception/module-rest/tree/1.4.2" - }, - "time": "2021-11-18T18:58:15+00:00" - }, - { - "name": "codeception/module-webdriver", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-webdriver.git", - "reference": "e22ac7da756df659df6dd4fac2dff9c859e30131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-webdriver/zipball/e22ac7da756df659df6dd4fac2dff9c859e30131", - "reference": "e22ac7da756df659df6dd4fac2dff9c859e30131", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.0", - "php": ">=5.6.0 <9.0", - "php-webdriver/webdriver": "^1.8.0" - }, - "suggest": { - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Zaahid Bateson" - } - ], - "description": "WebDriver module for Codeception", - "homepage": "http://codeception.com/", - "keywords": [ - "acceptance-testing", - "browser-testing", - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/module-webdriver/issues", - "source": "https://github.com/Codeception/module-webdriver/tree/1.4.1" - }, - "time": "2022-09-12T05:09:51+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "6.8.4", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "6267fb4e647da0e708b3aef181ff679a64433d67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/6267fb4e647da0e708b3aef181ff679a64433d67", - "reference": "6267fb4e647da0e708b3aef181ff679a64433d67", - "shasum": "" - }, - "require": { - "phpunit/php-code-coverage": ">=4.0.4 <6.0", - "phpunit/phpunit": ">=6.5.13 <7.0", - "sebastian/comparator": ">=1.2.4 <3.0", - "sebastian/diff": ">=1.4 <4.0" - }, - "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "PHPUnit classes used by Codeception", - "support": { - "issues": "https://github.com/Codeception/phpunit-wrapper/issues", - "source": "https://github.com/Codeception/phpunit-wrapper/tree/6.8.4" - }, - "time": "2022-05-23T05:56:13+00:00" - }, - { - "name": "codeception/stub", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "eea518711d736eab838c1274593c4568ec06b23d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/eea518711d736eab838c1274593c4568ec06b23d", - "reference": "eea518711d736eab838c1274593c4568ec06b23d", - "shasum": "" - }, - "require": { - "codeception/phpunit-wrapper": "^6.6.1 | ^7.7.1 | ^8.0.3", - "phpunit/phpunit": ">=6.5 <9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "support": { - "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/master" - }, - "time": "2019-08-10T16:20:53+00:00" - }, - { - "name": "codeception/util-universalframework", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/util-universalframework.git", - "reference": "cc381f364c6d24f9b9c7b70a4c724949725f491a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/util-universalframework/zipball/cc381f364c6d24f9b9c7b70a4c724949725f491a", - "reference": "cc381f364c6d24f9b9c7b70a4c724949725f491a", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gintautas Miselis" - } - ], - "description": "Mock framework module used in internal Codeception tests", - "homepage": "http://codeception.com/", - "support": { - "issues": "https://github.com/Codeception/util-universalframework/issues", - "source": "https://github.com/Codeception/util-universalframework/tree/1.0.0" - }, - "time": "2019-09-22T06:06:49+00:00" - }, - { - "name": "dg/mysql-dump", - "version": "v1.5.1", - "source": { - "type": "git", - "url": "https://github.com/dg/MySQL-dump.git", - "reference": "e0e287b715b43293773a8b0edf8514f606e01780" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dg/MySQL-dump/zipball/e0e287b715b43293773a8b0edf8514f606e01780", - "reference": "e0e287b715b43293773a8b0edf8514f606e01780", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "David Grudl", - "homepage": "http://davidgrudl.com" - } - ], - "description": "MySQL database dump.", - "homepage": "https://github.com/dg/MySQL-dump", - "keywords": [ - "mysql" - ], - "support": { - "source": "https://github.com/dg/MySQL-dump/tree/master" - }, - "time": "2019-09-10T21:36:25+00:00" - }, - { - "name": "doctrine/inflector", - "version": "2.0.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", - "homepage": "https://www.doctrine-project.org/projects/inflector.html", - "keywords": [ - "inflection", - "inflector", - "lowercase", - "manipulation", - "php", - "plural", - "singular", - "strings", - "uppercase", - "words" - ], - "support": { - "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.6" - }, - "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%2Finflector", - "type": "tidelift" - } - ], - "time": "2022-10-20T09:10:12+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": "guzzlehttp/guzzle", - "version": "7.5.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2022-08-28T15:39:27+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "1.5.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2022-08-28T14:55:35+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.4.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "67c26b443f348a51926030c83481b85718457d3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", - "reference": "67c26b443f348a51926030c83481b85718457d3d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2022-10-26T14:07:24+00:00" - }, - { - "name": "illuminate/collections", - "version": "v8.83.27", - "source": { - "type": "git", - "url": "https://github.com/illuminate/collections.git", - "reference": "705a4e1ef93cd492c45b9b3e7911cccc990a07f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/705a4e1ef93cd492c45b9b3e7911cccc990a07f4", - "reference": "705a4e1ef93cd492c45b9b3e7911cccc990a07f4", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "php": "^7.3|^8.0" - }, - "suggest": { - "symfony/var-dumper": "Required to use the dump method (^5.4)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "files": [ - "helpers.php" - ], - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Collections package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2022-06-23T15:29:49+00:00" - }, - { - "name": "illuminate/contracts", - "version": "v8.83.27", - "source": { - "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "5e0fd287a1b22a6b346a9f7cd484d8cf0234585d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/5e0fd287a1b22a6b346a9f7cd484d8cf0234585d", - "reference": "5e0fd287a1b22a6b346a9f7cd484d8cf0234585d", - "shasum": "" - }, - "require": { - "php": "^7.3|^8.0", - "psr/container": "^1.0", - "psr/simple-cache": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Contracts\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Contracts package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2022-01-13T14:47:47+00:00" - }, - { - "name": "illuminate/macroable", - "version": "v8.83.27", - "source": { - "type": "git", - "url": "https://github.com/illuminate/macroable.git", - "reference": "aed81891a6e046fdee72edd497f822190f61c162" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/aed81891a6e046fdee72edd497f822190f61c162", - "reference": "aed81891a6e046fdee72edd497f822190f61c162", - "shasum": "" - }, - "require": { - "php": "^7.3|^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Macroable package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2021-11-16T13:57:03+00:00" - }, - { - "name": "illuminate/support", - "version": "v8.83.27", - "source": { - "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "1c79242468d3bbd9a0f7477df34f9647dde2a09b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/1c79242468d3bbd9a0f7477df34f9647dde2a09b", - "reference": "1c79242468d3bbd9a0f7477df34f9647dde2a09b", - "shasum": "" - }, - "require": { - "doctrine/inflector": "^1.4|^2.0", - "ext-json": "*", - "ext-mbstring": "*", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "nesbot/carbon": "^2.53.1", - "php": "^7.3|^8.0", - "voku/portable-ascii": "^1.6.1" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "suggest": { - "illuminate/filesystem": "Required to use the composer class (^8.0).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3|^2.0.2).", - "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).", - "symfony/process": "Required to use the composer class (^5.4).", - "symfony/var-dumper": "Required to use the dd function (^5.4).", - "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "files": [ - "helpers.php" - ], - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Support package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2022-09-21T21:30:03+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.2.12", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" - }, - "time": "2022-04-13T08:02:27+00:00" - }, - { - "name": "lucatume/di52", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/lucatume/di52.git", - "reference": "01d080d95420d5f880ab1b888d51d7060d727a25" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lucatume/di52/zipball/01d080d95420d5f880ab1b888d51d7060d727a25", - "reference": "01d080d95420d5f880ab1b888d51d7060d727a25", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=5.6", - "psr/container": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "type": "library", - "autoload": { - "files": [ - "aliases.php" - ], - "psr-4": { - "lucatume\\DI52\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-3.0" - ], - "authors": [ - { - "name": "Luca Tumedei", - "email": "luca@theaveragedev.com" - } - ], - "description": "A PHP 5.6 compatible dependency injection container.", - "support": { - "issues": "https://github.com/lucatume/di52/issues", - "source": "https://github.com/lucatume/di52/tree/3.1.0" - }, - "time": "2023-01-28T09:14:45+00:00" - }, - { - "name": "lucatume/wp-browser", - "version": "3.1.6", - "source": { - "type": "git", - "url": "https://github.com/lucatume/wp-browser.git", - "reference": "5cefdab50d16f69447c48e5a8668d67d4892d6ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lucatume/wp-browser/zipball/5cefdab50d16f69447c48e5a8668d67d4892d6ef", - "reference": "5cefdab50d16f69447c48e5a8668d67d4892d6ef", - "shasum": "" - }, - "require": { - "antecedent/patchwork": "^2.0", - "bordoni/phpass": "^0.3", - "codeception/codeception": "^2.5 || ^3.0 || ^4.0", - "dg/mysql-dump": "^1.3", - "ext-fileinfo": "*", - "ext-json": "*", - "ext-pdo": "*", - "mikehaertl/php-shellcommand": "^1.6", - "mikemclin/laravel-wp-password": "~2.0.0", - "php": ">=5.6.0", - "vria/nodiacritic": "^0.1.2", - "wp-cli/wp-cli": ">=2.0 <3.0.0", - "zordius/lightncandy": "^1.2" - }, - "conflict": { - "codeception/module-asserts": ">=2.0", - "codeception/module-cli": ">=2.0", - "codeception/module-db": ">=2.0", - "codeception/module-filesystem": ">=2.0", - "codeception/module-phpbrowser": ">=2.0", - "codeception/module-webdriver": ">=2.0", - "codeception/util-universalframework": ">=2.0" - }, - "require-dev": { - "erusev/parsedown": "^1.7", - "ext-pcntl": "*", - "ext-sockets": "*", - "gumlet/php-image-resize": "^1.6", - "lucatume/codeception-snapshot-assertions": "^0.2", - "mikey179/vfsstream": "^1.6", - "victorjonsson/markdowndocs": "dev-master", - "vlucas/phpdotenv": "^3.0", - "wp-cli/wp-cli-bundle": "*" - }, - "suggest": { - "codeception/module-asserts:^1.0": "Codeception 4.0 compatibility.", - "codeception/module-cli:^1.0": "Codeception 4.0 compatibility; required by the WPCLI module.", - "codeception/module-db:^1.0": "Codeception 4.0 compatibility; required by the WPDb module, PHP 5.6 compatible version.", - "codeception/module-filesystem:^1.0": "Codeception 4.0 compatibility; required by the WPFilesystem module.", - "codeception/module-phpbrowser:^1.0": "Codeception 4.0 compatibility; required by the WPBrowser module.", - "codeception/module-webdriver:^1.0": "Codeception 4.0 compatibility; required by the WPWebDriver module.", - "codeception/util-universalframework:^1.0": "Codeception 4.0 compatibility; required by the WordPress framework module.", - "gumlet/php-image-resize": "To handle runtime image modification in the WPDb::haveAttachmentInDatabase method.", - "vlucas/phpdotenv:^4.0": "To manage more complex environment file based configuration of the suites." - }, - "type": "library", - "extra": { - "_hash": "484f861f69198089cab0e642f27e5653" - }, - "autoload": { - "files": [ - "src/tad/WPBrowser/utils.php", - "src/tad/WPBrowser/wp-polyfills.php" - ], - "psr-4": { - "tad\\": "src/tad", - "Codeception\\": "src/Codeception", - "lucatume\\WPBrowser\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "theAverageDev (Luca Tumedei)", - "email": "luca@theaveragedev.com", - "homepage": "http://theaveragedev.com", - "role": "Developer" - } - ], - "description": "WordPress extension of the PhpBrowser class.", - "homepage": "http://github.com/lucatume/wp-browser", - "keywords": [ - "codeception", - "wordpress" - ], - "support": { - "issues": "https://github.com/lucatume/wp-browser/issues", - "source": "https://github.com/lucatume/wp-browser/tree/3.1.6" - }, - "funding": [ - { - "url": "https://github.com/lucatume", - "type": "github" - } - ], - "time": "2022-04-28T06:45:08+00:00" - }, - { - "name": "mikehaertl/php-shellcommand", - "version": "1.6.4", - "source": { - "type": "git", - "url": "https://github.com/mikehaertl/php-shellcommand.git", - "reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mikehaertl/php-shellcommand/zipball/3488d7803df1e8f1a343d3d0ca452d527ad8d5e5", - "reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5", - "shasum": "" - }, - "require": { - "php": ">= 5.3.0" - }, - "require-dev": { - "phpunit/phpunit": ">4.0 <=9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "mikehaertl\\shellcommand\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Härtl", - "email": "haertl.mike@gmail.com" - } - ], - "description": "An object oriented interface to shell commands", - "keywords": [ - "shell" - ], - "support": { - "issues": "https://github.com/mikehaertl/php-shellcommand/issues", - "source": "https://github.com/mikehaertl/php-shellcommand/tree/1.6.4" - }, - "time": "2021-03-17T06:54:33+00:00" - }, - { - "name": "mikemclin/laravel-wp-password", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/mikemclin/laravel-wp-password.git", - "reference": "5225c95f75aa0a5ad4040ec2074d1c8d7f10b5f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mikemclin/laravel-wp-password/zipball/5225c95f75aa0a5ad4040ec2074d1c8d7f10b5f4", - "reference": "5225c95f75aa0a5ad4040ec2074d1c8d7f10b5f4", - "shasum": "" - }, - "require": { - "bordoni/phpass": "0.3.*", - "illuminate/support": ">=4.0.0", - "php": ">=5.3.0" - }, - "require-dev": { - "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^2.2" - }, - "type": "laravel-package", - "extra": { - "laravel": { - "providers": [ - "MikeMcLin\\WpPassword\\WpPasswordProvider" - ], - "aliases": { - "WpPassword": "MikeMcLin\\WpPassword\\Facades\\WpPassword" - } - } - }, - "autoload": { - "psr-4": { - "MikeMcLin\\WpPassword\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike McLin", - "email": "mike@mikemclin.com", - "homepage": "http://mikemclin.net" - } - ], - "description": "Laravel package that checks and creates WordPress password hashes", - "homepage": "https://github.com/mikemclin/laravel-wp-password", - "keywords": [ - "hashing", - "laravel", - "password", - "wordpress" - ], - "support": { - "issues": "https://github.com/mikemclin/laravel-wp-password/issues", - "source": "https://github.com/mikemclin/laravel-wp-password/tree/2.0.3" - }, - "time": "2021-09-30T13:48:57+00:00" - }, - { - "name": "mustache/mustache", - "version": "v2.14.2", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e62b7c3849d22ec55f3ec425507bf7968193a6cb", - "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "support": { - "issues": "https://github.com/bobthecow/mustache.php/issues", - "source": "https://github.com/bobthecow/mustache.php/tree/v2.14.2" - }, - "time": "2022-08-23T13:07:01+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": "nesbot/carbon", - "version": "2.66.0", - "source": { - "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "496712849902241f04902033b0441b269effe001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", - "reference": "496712849902241f04902033b0441b269effe001", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.1.8 || ^8.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", - "friendsofphp/php-cs-fixer": "^3.0", - "kylekatarnls/multi-tester": "^2.0", - "ondrejmirtes/better-reflection": "*", - "phpmd/phpmd": "^2.9", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.99 || ^1.7.14", - "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", - "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", - "squizlabs/php_codesniffer": "^3.4" - }, - "bin": [ - "bin/carbon" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-3.x": "3.x-dev", - "dev-master": "2.x-dev" - }, - "laravel": { - "providers": [ - "Carbon\\Laravel\\ServiceProvider" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "psr-4": { - "Carbon\\": "src/Carbon/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "https://markido.com" - }, - { - "name": "kylekatarnls", - "homepage": "https://github.com/kylekatarnls" - } - ], - "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", - "keywords": [ - "date", - "datetime", - "time" - ], - "support": { - "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" - }, - "funding": [ - { - "url": "https://github.com/sponsors/kylekatarnls", - "type": "github" - }, - { - "url": "https://opencollective.com/Carbon#sponsor", - "type": "opencollective" - }, - { - "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", - "type": "tidelift" - } - ], - "time": "2023-01-29T18:53:47+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.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/master" - }, - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.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/master" - }, - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "php-stubs/wordpress-stubs", - "version": "v6.1.1", - "source": { - "type": "git", - "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "601c429ba8d91ef50a2a1bec05a7cd38b88064ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/601c429ba8d91ef50a2a1bec05a7cd38b88064ff", - "reference": "601c429ba8d91ef50a2a1bec05a7cd38b88064ff", - "shasum": "" - }, - "require-dev": { - "nikic/php-parser": "< 4.12.0", - "php": "~7.3 || ~8.0", - "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.3", - "phpstan/phpstan": "^1.2" - }, - "suggest": { - "paragonie/sodium_compat": "Pure PHP implementation of libsodium", - "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "WordPress function and class declaration stubs for static analysis.", - "homepage": "https://github.com/php-stubs/wordpress-stubs", - "keywords": [ - "PHPStan", - "static analysis", - "wordpress" - ], - "support": { - "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.1.1" - }, - "time": "2023-02-09T11:10:35+00:00" - }, - { - "name": "php-webdriver/webdriver", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "3ea4f924afb43056bf9c630509e657d951608563" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3ea4f924afb43056bf9c630509e657d951608563", - "reference": "3ea4f924afb43056bf9c630509e657d951608563", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-zip": "*", - "php": "^7.3 || ^8.0", - "symfony/polyfill-mbstring": "^1.12", - "symfony/process": "^5.0 || ^6.0" - }, - "replace": { - "facebook/webdriver": "*" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.20.0", - "ondram/ci-detector": "^4.0", - "php-coveralls/php-coveralls": "^2.4", - "php-mock/php-mock-phpunit": "^2.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.5", - "symfony/var-dumper": "^5.0 || ^6.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "autoload": { - "files": [ - "lib/Exception/TimeoutException.php" - ], - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", - "homepage": "https://github.com/php-webdriver/php-webdriver", - "keywords": [ - "Chromedriver", - "geckodriver", - "php", - "selenium", - "webdriver" - ], - "support": { - "issues": "https://github.com/php-webdriver/php-webdriver/issues", - "source": "https://github.com/php-webdriver/php-webdriver/tree/1.14.0" - }, - "time": "2023-02-09T12:12:19+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": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpstan/phpstan", - "version": "1.9.17", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "204e459e7822f2c586463029f5ecec31bb45a1f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/204e459e7822f2c586463029f5ecec31bb45a1f2", - "reference": "204e459e7822f2c586463029f5ecec31bb45a1f2", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.17" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" - } - ], - "time": "2023-02-08T12:25:00+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/5.3" - }, - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.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": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "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/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "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": "sb@sebastian-bergmann.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/master" - }, - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "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": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" - }, - "abandoned": true, - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.14", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "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/6.5.14" - }, - "time": "2019-02-01T05:22:47+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/5.0.10" - }, - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/master" - }, - "time": "2020-06-29T06:28:15+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" - }, - "time": "2019-04-30T12:38:16+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], - "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" - }, - "time": "2017-10-23T01:57:42+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "rmccue/requests", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/WordPress/Requests.git", - "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress/Requests/zipball/82e6936366eac3af4d836c18b9d8c31028fe4cd5", - "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcompatibility/php-compatibility": "^9.0", - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5", - "requests/test-server": "dev-master", - "squizlabs/php_codesniffer": "^3.5", - "wp-coding-standards/wpcs": "^2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/WordPress/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ], - "support": { - "issues": "https://github.com/WordPress/Requests/issues", - "source": "https://github.com/WordPress/Requests/tree/v1.8.1" - }, - "time": "2021-06-04T09:56:25+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-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/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "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/master" - }, - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "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": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/master" - }, - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-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/master" - }, - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", - "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-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": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:00:17+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "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": "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/2.0.0" - }, - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-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/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-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/1.1.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:37:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-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/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:34:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-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/master" - }, - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "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": "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/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "softcreatr/jsonpath", - "version": "0.7.6", - "source": { - "type": "git", - "url": "https://github.com/SoftCreatR/JSONPath.git", - "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/e04c02cb78bcc242c69d17dac5b29436bf3e1076", - "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=7.1,<8.0" - }, - "replace": { - "flow/jsonpath": "*" - }, - "require-dev": { - "phpunit/phpunit": ">=7.0", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Flow\\JSONPath\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Frank", - "email": "stephen@flowsa.com", - "homepage": "https://prismaticbytes.com", - "role": "Developer" - }, - { - "name": "Sascha Greuel", - "email": "hello@1-2.dev", - "homepage": "https://1-2.dev", - "role": "Developer" - } - ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", - "support": { - "email": "hello@1-2.dev", - "forum": "https://github.com/SoftCreatR/JSONPath/discussions", - "issues": "https://github.com/SoftCreatR/JSONPath/issues", - "source": "https://github.com/SoftCreatR/JSONPath" - }, - "funding": [ - { - "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", - "type": "custom" - }, - { - "url": "https://github.com/softcreatr", - "type": "github" - } - ], - "time": "2022-09-27T09:27:12+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "572b9e03741051b97c316f65f8c361eed08fdb14" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/572b9e03741051b97c316f65f8c361eed08fdb14", - "reference": "572b9e03741051b97c316f65f8c361eed08fdb14", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.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/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/css-selector", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "f4a7d150f5b9e8f974f6f127d8167e420d11fc62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4a7d150f5b9e8f974f6f127d8167e420d11fc62", - "reference": "f4a7d150f5b9e8f974f6f127d8167e420d11fc62", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.4.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/dom-crawler", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "224a1820e7669babdd85970230ed72bd6e342ad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/224a1820e7669babdd85970230ed72bd6e342ad4", - "reference": "224a1820e7669babdd85970230ed72bd6e342ad4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "masterminds/html5": "<2.6" - }, - "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.4.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/event-dispatcher", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abf49cc084c087d94b4cb939c3f3672971784e0c", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.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/event-dispatcher-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-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\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.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/finder", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6071aebf810ad13fe8200c224f36103abb37cf1f", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.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/process", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/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/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": "symfony/translation", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^2.3" - }, - "conflict": { - "symfony/config": "<4.4", - "symfony/console": "<5.3", - "symfony/dependency-injection": "<5.0", - "symfony/http-kernel": "<5.0", - "symfony/twig-bundle": "<5.0", - "symfony/yaml": "<4.4" - }, - "provide": { - "symfony/translation-implementation": "2.3" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/translation/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/translation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-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\\Translation\\": "" - } - }, - "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 translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-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-06-27T16:58:25+00:00" - }, - { - "name": "symfony/yaml", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/71c05db20cb9b54d381a28255f17580e2b7e36a5", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<5.3" - }, - "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.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-10T18:51:14+00:00" - }, - { - "name": "szepeviktor/phpstan-wordpress", - "version": "v1.1.7", - "source": { - "type": "git", - "url": "https://github.com/szepeviktor/phpstan-wordpress.git", - "reference": "979dcb81a01942b576b9fbf72dcb9515c57a4aa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/979dcb81a01942b576b9fbf72dcb9515c57a4aa8", - "reference": "979dcb81a01942b576b9fbf72dcb9515c57a4aa8", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0", - "phpstan/phpstan": "^1.8.7", - "symfony/polyfill-php73": "^1.12.0" - }, - "require-dev": { - "composer/composer": "^2.1.14", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpstan/phpstan-strict-rules": "^1.2", - "phpunit/phpunit": "^8.0 || ^9.0", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.8" - }, - "type": "phpstan-extension", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "psr-4": { - "SzepeViktor\\PHPStan\\WordPress\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "WordPress extensions for PHPStan", - "keywords": [ - "PHPStan", - "code analyse", - "code analysis", - "static analysis", - "wordpress" - ], - "support": { - "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", - "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v1.1.7" - }, - "funding": [ - { - "url": "https://www.paypal.me/szepeviktor", - "type": "custom" - }, - { - "url": "https://github.com/szepeviktor", - "type": "github" - } - ], - "time": "2023-02-04T13:10:27+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": "voku/portable-ascii", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a", - "shasum": "" - }, - "require": { - "php": ">=7.0.0" - }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" - }, - "suggest": { - "ext-intl": "Use Intl for transliterator_transliterate() support" - }, - "type": "library", - "autoload": { - "psr-4": { - "voku\\": "src/voku/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" - } - ], - "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "homepage": "https://github.com/voku/portable-ascii", - "keywords": [ - "ascii", - "clean", - "php" - ], - "support": { - "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/1.6.1" - }, - "funding": [ - { - "url": "https://www.paypal.me/moelleken", - "type": "custom" - }, - { - "url": "https://github.com/voku", - "type": "github" - }, - { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", - "type": "tidelift" - } - ], - "time": "2022-01-24T18:55:24+00:00" - }, - { - "name": "vria/nodiacritic", - "version": "0.1.2", - "source": { - "type": "git", - "url": "https://github.com/vria/nodiacritic.git", - "reference": "3efeb60fb2586fe3ce8ff0f3c122d380717b8b07" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vria/nodiacritic/zipball/3efeb60fb2586fe3ce8ff0f3c122d380717b8b07", - "reference": "3efeb60fb2586fe3ce8ff0f3c122d380717b8b07", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "4.8.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "VRia\\Utils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0" - ], - "authors": [ - { - "name": "Riabchenko Vlad", - "email": "contact@vria.eu", - "homepage": "http://vria.eu" - } - ], - "description": "Tiny helper function that removes all diacritical signs from characters", - "homepage": "https://github.com/vria/nodiacritic", - "keywords": [ - "accent", - "diacritic", - "filter", - "string", - "text" - ], - "support": { - "email": "contact@vria.eu", - "issues": "https://github.com/vria/nodiacritic/issues", - "source": "https://github.com/vria/nodiacritic/tree/0.1.2" - }, - "time": "2016-09-17T22:03:11+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "wp-cli/mustangostang-spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/spyc.git", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/spyc/zipball/6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "includes/functions.php" - ], - "psr-4": { - "Mustangostang\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", - "homepage": "https://github.com/mustangostang/spyc/", - "support": { - "source": "https://github.com/wp-cli/spyc/tree/autoload" - }, - "time": "2017-04-25T11:26:20+00:00" - }, - { - "name": "wp-cli/php-cli-tools", - "version": "v0.11.17", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/php-cli-tools.git", - "reference": "f6be76b7c4ee2ef93c9531b8a37bdb7ce42c3728" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/f6be76b7c4ee2ef93c9531b8a37bdb7ce42c3728", - "reference": "f6be76b7c4ee2ef93c9531b8a37bdb7ce42c3728", - "shasum": "" - }, - "require": { - "php": ">= 5.3.0" - }, - "require-dev": { - "roave/security-advisories": "dev-latest", - "wp-cli/wp-cli-tests": "^3.1.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.11.x-dev" - } - }, - "autoload": { - "files": [ - "lib/cli/cli.php" - ], - "psr-0": { - "cli": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@handbuilt.co", - "role": "Maintainer" - }, - { - "name": "James Logsdon", - "email": "jlogsdon@php.net", - "role": "Developer" - } - ], - "description": "Console utilities for PHP", - "homepage": "http://github.com/wp-cli/php-cli-tools", - "keywords": [ - "cli", - "console" - ], - "support": { - "issues": "https://github.com/wp-cli/php-cli-tools/issues", - "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.11.17" - }, - "time": "2023-01-12T01:18:21+00:00" - }, - { - "name": "wp-cli/wp-cli", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/wp-cli.git", - "reference": "1ddc754f1c15e56fb2cdd1a4e82bd0ec6ca32a76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/1ddc754f1c15e56fb2cdd1a4e82bd0ec6ca32a76", - "reference": "1ddc754f1c15e56fb2cdd1a4e82bd0ec6ca32a76", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "mustache/mustache": "^2.14.1", - "php": "^5.6 || ^7.0 || ^8.0", - "rmccue/requests": "^1.8", - "symfony/finder": ">2.7", - "wp-cli/mustangostang-spyc": "^0.6.3", - "wp-cli/php-cli-tools": "~0.11.2" - }, - "require-dev": { - "roave/security-advisories": "dev-latest", - "wp-cli/db-command": "^1.3 || ^2", - "wp-cli/entity-command": "^1.2 || ^2", - "wp-cli/extension-command": "^1.1 || ^2", - "wp-cli/package-command": "^1 || ^2", - "wp-cli/wp-cli-tests": "^3.1.6" - }, - "suggest": { - "ext-readline": "Include for a better --prompt implementation", - "ext-zip": "Needed to support extraction of ZIP archives when doing downloads or updates" - }, - "bin": [ - "bin/wp", - "bin/wp.bat" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev" - } - }, - "autoload": { - "psr-0": { - "WP_CLI\\": "php/" - }, - "classmap": [ - "php/class-wp-cli.php", - "php/class-wp-cli-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "WP-CLI framework", - "homepage": "https://wp-cli.org", - "keywords": [ - "cli", - "wordpress" - ], - "support": { - "docs": "https://make.wordpress.org/cli/handbook/", - "issues": "https://github.com/wp-cli/wp-cli/issues", - "source": "https://github.com/wp-cli/wp-cli" - }, - "time": "2022-10-17T23:10:42+00:00" - }, - { - "name": "zordius/lightncandy", - "version": "v1.2.6", - "source": { - "type": "git", - "url": "https://github.com/zordius/lightncandy.git", - "reference": "b451f73e8b5c73e62e365997ba3c993a0376b72a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zordius/lightncandy/zipball/b451f73e8b5c73e62e365997ba3c993a0376b72a", - "reference": "b451f73e8b5c73e62e365997ba3c993a0376b72a", - "shasum": "" - }, - "require": { - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": ">=7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.5-dev" - } - }, - "autoload": { - "psr-4": { - "LightnCandy\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Zordius Chen", - "email": "zordius@gmail.com" - } - ], - "description": "An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ).", - "homepage": "https://github.com/zordius/lightncandy", - "keywords": [ - "handlebars", - "logicless", - "mustache", - "php", - "template" - ], - "support": { - "issues": "https://github.com/zordius/lightncandy/issues", - "source": "https://github.com/zordius/lightncandy/tree/v1.2.6" - }, - "time": "2021-07-11T04:52:41+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=7.1" - }, - "platform-dev": [], - "plugin-api-version": "2.2.0" -} diff --git a/src/Uplink/API/Client.php b/src/Uplink/API/Client.php index bf8659ff..bff9da37 100644 --- a/src/Uplink/API/Client.php +++ b/src/Uplink/API/Client.php @@ -25,7 +25,7 @@ class Client { * * @var string */ - public static $api_root = '/api/plugins/v2/'; + protected $api_root = '/api/plugins/v2/'; /** * Base URL for the license key server. @@ -34,7 +34,7 @@ class Client { * * @var string */ - public static $base_url = 'https://pue.theeventscalendar.com'; + protected $base_url = 'https://pue.theeventscalendar.com'; /** * Container. @@ -55,11 +55,11 @@ public function __construct() { $this->container = Config::get_container(); if ( defined( 'STELLARWP_UPLINK_API_BASE_URL' ) && STELLARWP_UPLINK_API_BASE_URL ) { - static::$base_url = preg_replace( '!/$!', '', STELLARWP_UPLINK_API_BASE_URL ); + $this->base_url = preg_replace( '!/$!', '', STELLARWP_UPLINK_API_BASE_URL ); } if ( defined( 'STELLARWP_UPLINK_API_ROOT' ) && STELLARWP_UPLINK_API_ROOT ) { - static::$api_root = trailingslashit( STELLARWP_UPLINK_API_ROOT ); + $this->api_root = trailingslashit( STELLARWP_UPLINK_API_ROOT ); } } @@ -115,7 +115,7 @@ public function get_api_base_url() : string { * * @param string $base_url Base URL. */ - return apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/api_get_base_url', static::$base_url ); + return apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/api_get_base_url', $this->base_url ); } /** @@ -164,7 +164,7 @@ protected function request( $method, $endpoint, $args ) { */ $request_args = apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/api_request_args', $request_args, $endpoint, $args ); - $url = static::$base_url . static::$api_root . $endpoint; + $url = $this->base_url . $this->api_root . $endpoint; $response = wp_remote_get( $url, $request_args ); $response_body = wp_remote_retrieve_body( $response ); @@ -179,9 +179,7 @@ protected function request( $method, $endpoint, $args ) { * @param string $endpoint API endpoint. * @param array $args API arguments. */ - $result = apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/api_response', $result, $endpoint, $args ); - - return $result; + return apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/api_response', $result, $endpoint, $args ); } /** @@ -197,8 +195,6 @@ protected function request( $method, $endpoint, $args ) { * @return mixed */ public function validate_license( Resource $resource, string $key = null, string $validation_type = 'local', bool $force = false ) { - $results = []; - /** @var Data */ $site_data = $this->container->get( Data::class ); $args = $resource->get_validation_args(); diff --git a/src/Uplink/API/V3/Auth/Auth_Url.php b/src/Uplink/API/V3/Auth/Auth_Url.php new file mode 100644 index 00000000..82b0e9f7 --- /dev/null +++ b/src/Uplink/API/V3/Auth/Auth_Url.php @@ -0,0 +1,56 @@ +client = $client; + } + + /** + * Retrieve an Origin's auth url, if it exists. + * + * @param string $slug The product slug. + * + * @return string + */ + public function get( string $slug ): string { + $response = $this->client->get( 'tokens/auth_url', [ + 'slug' => $slug, + ] ); + + if ( $response instanceof WP_Error ) { + if ( $this->is_wp_debug() ) { + error_log( sprintf( + 'Token auth failed for slug: "%s". Errors: %s', + $slug, + implode( ', ', $response->get_error_messages() ) + ) ); + } + + return ''; + } + + return $response['body']['data']['auth_url'] ?? ''; + } + +} diff --git a/src/Uplink/API/V3/Auth/Auth_Url_Cache_Decorator.php b/src/Uplink/API/V3/Auth/Auth_Url_Cache_Decorator.php new file mode 100644 index 00000000..74195e81 --- /dev/null +++ b/src/Uplink/API/V3/Auth/Auth_Url_Cache_Decorator.php @@ -0,0 +1,76 @@ +auth_url = $auth_url; + $this->expiration = $expiration; + } + + /** + * Cache the auth url response. + * + * @param string $slug The product slug. + * + * @throws InvalidArgumentException + * + * @return string + */ + public function get( string $slug ): string { + if ( ! $slug ) { + throw new InvalidArgumentException( __( 'The Product Slug cannot be empty', '%TEXTDOMAIN%' ) ); + } + + $transient = $this->build_transient( $slug ); + + $url = get_transient( $transient ); + + if ( $url !== false ) { + return $url; + } + + $url = $this->auth_url->get( $slug ); + + // We'll cache empty auth URLs to prevent further remote requests. + set_transient( $transient, $url, $this->expiration ); + + return $url; + } + + /** + * Build the transient key based on the provided slug. + * + * @param string $slug + * + * @return string + */ + private function build_transient( string $slug ): string { + return self::TRANSIENT_PREFIX . str_replace( '-', '_', $slug ); + } + +} diff --git a/src/Uplink/API/V3/Auth/Contracts/Auth_Url.php b/src/Uplink/API/V3/Auth/Contracts/Auth_Url.php new file mode 100644 index 00000000..bf515649 --- /dev/null +++ b/src/Uplink/API/V3/Auth/Contracts/Auth_Url.php @@ -0,0 +1,16 @@ +client = $client; + } + + /** + * Manually check if a license is authorized. + * + * @see is_authorized() + * + * @param string $license The license key. + * @param string $token The stored token. + * @param string $domain The user's domain. + * + * @return bool + */ + public function is_authorized( string $license, string $token, string $domain ): bool { + $response = $this->client->get( 'tokens/auth', [ + 'license' => $license, + 'token' => $token, + 'domain' => $domain, + ] ); + + if ( $response instanceof WP_Error ) { + if ( $this->is_wp_debug() ) { + error_log( sprintf( + __( 'Authorization error occurred: License: "%s", Token: "%s", Domain: "%s". Errors: %s', '%TEXTDOMAIN%' ), + $license, + $token, + $domain, + implode( ', ', $response->get_error_messages() ) + ) ); + } + + return false; + } + + return $response['response']['code'] === WP_Http::OK; + } + +} diff --git a/src/Uplink/API/V3/Client.php b/src/Uplink/API/V3/Client.php new file mode 100644 index 00000000..1191fbf0 --- /dev/null +++ b/src/Uplink/API/V3/Client.php @@ -0,0 +1,156 @@ + + */ + private $request_args; + + /** + * @var WP_Http + */ + private $wp_http; + + /** + * @param string $api_root + * @param string $base_url + * @param array $request_args + * @param WP_Http $wp_http + */ + public function __construct( string $api_root, string $base_url, array $request_args, WP_Http $wp_http ) { + $this->api_root = $api_root; + $this->base_url = $base_url; + $this->request_args = $request_args; + $this->wp_http = $wp_http; + } + + /** + * Perform a GET request. + * + * @param string $endpoint + * @param array $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function get( string $endpoint, array $params = [] ) { + $args = array_merge( $this->request_args, [ + 'body' => $params, + ] ); + + return $this->request( $endpoint, 'GET', $args ); + } + + /** + * Perform a POST request. + * + * @param string $endpoint + * @param array $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function post( string $endpoint, array $params = [] ) { + $args = array_merge( $this->request_args, [ + 'body' => $params, + ] ); + + return $this->request( $endpoint, 'POST', $args ); + } + + /** + * Perform any other request. + * + * @param string $endpoint + * @param string $method + * @param array $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function request( string $endpoint, string $method = 'GET', array $params = [] ) { + $url = $this->build_url( $endpoint ); + + $args = array_merge( $this->request_args, [ + 'method' => strtoupper( $method ), + ], $params ); + + $response = $this->wp_http->request( $url, $args ); + + if ( $response instanceof WP_Error ) { + return $response; + } + + $response['body'] = json_decode( $response['body'], true ); + + return $response; + } + + /** + * Build the complete URL from a provided endpoint. + * + * @param string $endpoint The relative endpoint. + * + * @return string + */ + private function build_url( string $endpoint ): string { + return rtrim( $this->base_url, '/' ) . trailingslashit( $this->api_root ) . ltrim( $endpoint, '/' ); + } + +} diff --git a/src/Uplink/API/V3/Contracts/Client_V3.php b/src/Uplink/API/V3/Contracts/Client_V3.php new file mode 100644 index 00000000..5bc28645 --- /dev/null +++ b/src/Uplink/API/V3/Contracts/Client_V3.php @@ -0,0 +1,72 @@ + $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function get( string $endpoint, array $params = [] ); + + + /** + * Perform a POST request. + * + * @param string $endpoint + * @param array $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function post( string $endpoint, array $params = [] ); + + /** + * Perform any other request. + * + * @param string $endpoint + * @param string $method + * @param array $params + * + * @return WP_Error|array{ + * 'body' : array, + * 'headers' : CaseInsensitiveDictionary, + * 'response' : array{ + * 'code' : int, + * 'message' : string, + * }, + * 'cookies' : array, + * 'filename' : string|null, + * 'http_response' : \WP_HTTP_Requests_Response + * } + */ + public function request( string $endpoint, string $method = 'GET', array $params = [] ); + +} diff --git a/src/Uplink/API/V3/Provider.php b/src/Uplink/API/V3/Provider.php new file mode 100644 index 00000000..ad2bb834 --- /dev/null +++ b/src/Uplink/API/V3/Provider.php @@ -0,0 +1,59 @@ +container->bind( Auth_Url::class, Auth_Url_Cache_Decorator::class ); + + $this->container->singleton( Client_V3::class, static function (): Client { + $prefix = 'stellarwp/uplink/' . Config::get_hook_prefix(); + $api_root = '/api/stellarwp/v3/'; + + if ( defined( 'STELLARWP_UPLINK_V3_API_ROOT' ) && STELLARWP_UPLINK_V3_API_ROOT ) { + $api_root = STELLARWP_UPLINK_V3_API_ROOT; + } + + $base_url = 'https://licensing.stellarwp.com'; + + if ( defined( 'STELLARWP_UPLINK_API_BASE_URL' ) && STELLARWP_UPLINK_API_BASE_URL ) { + $base_url = preg_replace( '!/$!', '', STELLARWP_UPLINK_API_BASE_URL ); + } + + /** + * Filter the V3 api root. + * + * @param string $api_root The base endpoint for the v3 API. + */ + $api_root = apply_filters( $prefix . '/v3/client/api_root', $api_root ); + + /** + * Filter the V3 api base URL. + * + * @param string $base_url The base URL for the v3 API. + */ + $base_url = apply_filters( $prefix . '/v3/client/base_url', $base_url ); + + $request_args = apply_filters( $prefix . '/v3/client/request_args', [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'timeout' => 15, // Seconds. + ] ); + + return new Client( $api_root, $base_url, $request_args, new WP_Http() ); + } ); + } + +} diff --git a/src/Uplink/Admin/Notice.php b/src/Uplink/Admin/Notice.php index 2ec8054a..df3b0194 100644 --- a/src/Uplink/Admin/Notice.php +++ b/src/Uplink/Admin/Notice.php @@ -16,12 +16,12 @@ class Notice { /** * @var array */ - protected array $saved_notices = []; + protected $saved_notices = []; /** * @var array */ - protected array $notices = []; + protected $notices = []; /** * @since 1.0.0 diff --git a/src/Uplink/Auth/Admin/Connect_Controller.php b/src/Uplink/Auth/Admin/Connect_Controller.php new file mode 100644 index 00000000..f74ebc26 --- /dev/null +++ b/src/Uplink/Auth/Admin/Connect_Controller.php @@ -0,0 +1,130 @@ +connector = $connector; + $this->notice = $notice; + $this->collection = $collection; + } + + /** + * Store the token data passed back from the Origin site. + * + * @action admin_init + */ + public function maybe_store_token_data(): void { + if ( ! is_admin() || wp_doing_ajax() ) { + return; + } + + if ( ! is_user_logged_in() ) { + return; + } + + $args = array_intersect_key( $_GET, [ + self::TOKEN => true, + self::NONCE => true, + self::LICENSE => true, + self::SLUG => true, + ] ); + + if ( ! $args ) { + return; + } + + if ( ! Nonce::verify( $args[ self::NONCE ] ?? '' ) ) { + $this->notice->add( new Notice( Notice::ERROR, + __( 'Unable to save token data: nonce verification failed.', '%TEXTDOMAIN%' ), + true + ) ); + + return; + } + + try { + if ( ! $this->connector->connect( $args[ self::TOKEN ] ?? '' ) ) { + $this->notice->add( new Notice( Notice::ERROR, + __( 'Error storing token.', '%TEXTDOMAIN%' ), + true + ) ); + + return; + } + } catch ( InvalidTokenException $e ) { + $this->notice->add( new Notice( Notice::ERROR, + sprintf( '%s.', $e->getMessage() ), + true + ) ); + + return; + } + + $license = $args[ self::LICENSE ] ?? ''; + $slug = $args[ self::SLUG ] ?? ''; + + // Store or override an existing license. + if ( $license && $slug ) { + if ( ! $this->collection->offsetExists( $slug ) ) { + $this->notice->add( new Notice( Notice::ERROR, + __( 'Plugin or Service slug not found.', '%TEXTDOMAIN%' ), + true + ) ); + + return; + } + + $plugin = $this->collection->offsetGet( $slug ); + + if ( ! $plugin->set_license_key( $license, 'network' ) ) { + $this->notice->add( new Notice( Notice::ERROR, + __( 'Error storing license key.', '%TEXTDOMAIN%' ), + true + ) ); + + return; + } + } + + $this->notice->add( + new Notice( Notice::SUCCESS, + __( 'Connected successfully.', '%TEXTDOMAIN%' ), + true + ) + ); + } +} diff --git a/src/Uplink/Auth/Admin/Disconnect_Controller.php b/src/Uplink/Auth/Admin/Disconnect_Controller.php new file mode 100644 index 00000000..8d61f096 --- /dev/null +++ b/src/Uplink/Auth/Admin/Disconnect_Controller.php @@ -0,0 +1,107 @@ +disconnect = $disconnect; + $this->notice = $notice; + } + + /** + * Disconnect (delete) a token if the user is allowed to. + * + * @action admin_init + * + * @return void + */ + public function maybe_disconnect(): void { + if ( empty( $_GET[ self::ARG ] ) || empty( $_GET['_wpnonce'] ) ) { + return; + } + + if ( ! is_admin() || wp_doing_ajax() ) { + return; + } + + if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), self::ARG ) ) { + if ( $this->disconnect->disconnect() ) { + $this->notice->add( + new Notice( Notice::SUCCESS, + __( 'Token disconnected.', '%TEXTDOMAIN%' ), + true + ) + ); + } else { + $this->notice->add( + new Notice( Notice::ERROR, + __( 'Unable to disconnect token, ensure you have admin permissions.', '%TEXTDOMAIN%' ), + true + ) + ); + } + } else { + $this->notice->add( + new Notice( Notice::ERROR, + __( 'Unable to disconnect token: nonce verification failed.', '%TEXTDOMAIN%' ), + true + ) + ); + } + + $this->maybe_redirect_back(); + } + + /** + * Attempts to redirect the user back to their previous dashboard page while + * ensuring that any "Connect" token query variables are removed if they immediately + * attempt to Disconnect after Connecting. This prevents them from automatically + * getting connected again if the nonce is still valid. + * + * This will ensure the Notices set above are displayed. + * + * @return void + */ + private function maybe_redirect_back(): void { + $referer = wp_get_referer(); + + if ( ! $referer ) { + return; + } + + $referer = remove_query_arg( + [ + Connect_Controller::TOKEN, + Connect_Controller::LICENSE, + Connect_Controller::SLUG, + Connect_Controller::NONCE, + ], + $referer + ); + + wp_safe_redirect( esc_url_raw( $referer ) ); + exit; + } + +} diff --git a/src/Uplink/Auth/Auth_Pipes/Multisite_Subfolder_Check.php b/src/Uplink/Auth/Auth_Pipes/Multisite_Subfolder_Check.php new file mode 100644 index 00000000..b480571b --- /dev/null +++ b/src/Uplink/Auth/Auth_Pipes/Multisite_Subfolder_Check.php @@ -0,0 +1,44 @@ +token_manager = $token_manager; + } + + /** + * Checks if a sub-site already has a network token. + * + * @param bool $can_auth + * @param Closure $next + * + * @return bool + */ + public function __invoke( bool $can_auth, Closure $next ): bool { + if ( ! is_multisite() ) { + return $next( $can_auth ); + } + + if ( is_main_site() ) { + return $next( $can_auth ); + } + + // Token already exists at the network level, don't authorize for this sub-site. + if ( $this->token_manager->get() ) { + return false; + } + + return $next( $can_auth ); + } + +} diff --git a/src/Uplink/Auth/Auth_Pipes/User_Check.php b/src/Uplink/Auth/Auth_Pipes/User_Check.php new file mode 100644 index 00000000..de5b9efa --- /dev/null +++ b/src/Uplink/Auth/Auth_Pipes/User_Check.php @@ -0,0 +1,36 @@ +pipeline = $pipeline; + } + + /** + * Runs the pipeline which executes a series of checks to determine if + * the user can use the authorize button on the current site. + * + * @see Provider::register_authorizer() + * + * @return bool + */ + public function can_auth(): bool { + return $this->pipeline->send( true )->thenReturn(); + } + +} diff --git a/src/Uplink/Auth/Nonce.php b/src/Uplink/Auth/Nonce.php new file mode 100644 index 00000000..300abfb7 --- /dev/null +++ b/src/Uplink/Auth/Nonce.php @@ -0,0 +1,84 @@ +expiration = $expiration; + } + + /** + * Verify a nonce. + * + * @param string $nonce The nonce token. + * + * @return bool + */ + public static function verify( string $nonce ): bool { + if ( ! $nonce ) { + return false; + } + + return $nonce === get_transient( Config::get_hook_prefix_underscored() . self::NONCE_SUFFIX ); + } + + /** + * Create or reuse a non-expired nonce. + * + * @return string + */ + public function create(): string { + $existing = get_transient( $this->key() ); + + if ( $existing ) { + return $existing; + } + + $nonce = wp_generate_password( 16, false ); + + set_transient( $this->key(), $nonce, $this->expiration ); + + return $nonce; + } + + /** + * Attach a nonce to a URL. + * + * @param string $url The existing URL to attach the nonce to. + * + * @return string + */ + public function create_url( string $url ): string { + $url = str_replace( '&', '&', $url ); + + return esc_html( add_query_arg( '_uplink_nonce', $this->create(), $url ) ); + } + + /** + * Get the transient key, combining the configured hook prefix with our suffix. + * + * @return string + */ + private function key(): string { + return Config::get_hook_prefix_underscored() . self::NONCE_SUFFIX; + } + +} diff --git a/src/Uplink/Auth/Provider.php b/src/Uplink/Auth/Provider.php new file mode 100644 index 00000000..1d213d4d --- /dev/null +++ b/src/Uplink/Auth/Provider.php @@ -0,0 +1,107 @@ +container->has( Config::TOKEN_OPTION_NAME ) ) { + return; + } + + $this->container->bind( + Token_Manager::class, + static function ( $c ) { + return new Token\Token_Manager( $c->get( Config::TOKEN_OPTION_NAME ) ); + } + ); + + $this->register_nonce(); + $this->register_authorizer(); + $this->register_auth_disconnect(); + $this->register_auth_connect(); + } + + /** + * Register nonce container definitions. + * + * @return void + */ + private function register_nonce(): void { + /** + * Filter how long the callback nonce is valid for. + * + * @note There is also an expiration time in the Uplink Origin plugin. + * + * Default: 35 minutes, to allow time for them to properly log in. + * + * @param int $expiration Nonce expiration time in seconds. + */ + $expiration = apply_filters( 'stellarwp/uplink/' . Config::get_hook_prefix() . '/auth/nonce_expiration', 2100 ); + $expiration = absint( $expiration ); + + $this->container->singleton( Nonce::class, new Nonce( $expiration ) ); + } + + /** + * Registers the Authorizer and the steps in order for the pipeline + * processing. + */ + private function register_authorizer(): void { + $this->container->singleton( + Network_Token_Check::class, + static function ( $c ) { + return new Network_Token_Check( $c->get( Token_Manager::class ) ); + } + ); + + $pipeline = ( new Pipeline( $this->container ) )->through( [ + User_Check::class, + Multisite_Subfolder_Check::class, + Network_Token_Check::class, + ] ); + + $this->container->singleton( + Authorizer::class, + static function () use ( $pipeline ) { + return new Authorizer( $pipeline ); + } + ); + } + + /** + * Register auth disconnection definitions and hooks. + * + * @return void + */ + private function register_auth_disconnect(): void { + $this->container->singleton( Disconnect_Controller::class, Disconnect_Controller::class ); + + add_action( 'admin_init', [ $this->container->get( Disconnect_Controller::class ), 'maybe_disconnect' ], 9, 0 ); + } + + /** + * Register auth connection definitions and hooks. + * + * @return void + */ + private function register_auth_connect(): void { + $this->container->singleton( Connect_Controller::class, Connect_Controller::class ); + + add_action( 'admin_init', [ $this->container->get( Connect_Controller::class ), 'maybe_store_token_data'], 9, 0 ); + } + +} diff --git a/src/Uplink/Auth/Token/Connector.php b/src/Uplink/Auth/Token/Connector.php new file mode 100644 index 00000000..b4ac265e --- /dev/null +++ b/src/Uplink/Auth/Token/Connector.php @@ -0,0 +1,50 @@ +authorizer = $authorizer; + $this->token_manager = $token_manager; + } + + /** + * Store a token if the user is allowed to. + * + * @throws InvalidTokenException + */ + public function connect( string $token ): bool { + if ( ! $this->authorizer->can_auth() ) { + return false; + } + + if ( ! $this->token_manager->validate( $token ) ) { + throw new InvalidTokenException( 'Invalid token format' ); + } + + return $this->token_manager->store( $token ); + } + +} diff --git a/src/Uplink/Auth/Token/Contracts/Token_Manager.php b/src/Uplink/Auth/Token/Contracts/Token_Manager.php new file mode 100644 index 00000000..695b1c3f --- /dev/null +++ b/src/Uplink/Auth/Token/Contracts/Token_Manager.php @@ -0,0 +1,54 @@ +authorizer = $authorizer; + $this->token_manager = $token_manager; + } + + /** + * Delete a token if the current user is allowed to. + */ + public function disconnect(): bool { + if ( ! $this->authorizer->can_auth() ) { + return false; + } + + return $this->token_manager->delete(); + } + +} diff --git a/src/Uplink/Auth/Token/Exceptions/InvalidTokenException.php b/src/Uplink/Auth/Token/Exceptions/InvalidTokenException.php new file mode 100644 index 00000000..045cbe16 --- /dev/null +++ b/src/Uplink/Auth/Token/Exceptions/InvalidTokenException.php @@ -0,0 +1,7 @@ +option_name = $option_name; + } + + /** + * Returns the option_name that is used to store tokens. + * + * @return string + */ + public function option_name(): string { + return $this->option_name; + } + + /** + * Validates a token is in the accepted UUIDv4 format. + * + * @param string $token + * + * @return bool + */ + public function validate( string $token ): bool { + $pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i'; + + return preg_match( $pattern, $token ) === 1; + } + + /** + * Store the token. + * + * @param string $token + * + * @return bool + */ + public function store( string $token ): bool { + if ( ! $token ) { + return false; + } + + // WordPress would otherwise return false if the items match. + if ( $token === $this->get() ) { + return true; + } + + return update_network_option( get_current_network_id(), $this->option_name, $token ); + } + + /** + * Get the token. + * + * @return string|null + */ + public function get(): ?string { + return get_network_option( get_current_network_id(), $this->option_name, null ); + } + + /** + * Revoke the token. + * + * @return bool + */ + public function delete(): bool { + // Already doesn't exist, WordPress would normally return false. + if ( $this->get() === null ) { + return true; + } + + return delete_network_option( get_current_network_id(), $this->option_name ); + } + +} diff --git a/src/Uplink/Components/Admin/Authorize_Button_Controller.php b/src/Uplink/Components/Admin/Authorize_Button_Controller.php new file mode 100644 index 00000000..9de6ec77 --- /dev/null +++ b/src/Uplink/Components/Admin/Authorize_Button_Controller.php @@ -0,0 +1,220 @@ +authorizer = $authorizer; + $this->token_manager = $token_manager; + $this->nonce = $nonce; + $this->auth_url_manager = $auth_url_manager; + } + + /** + * Renders the authorize-button view. + * + * @param array{slug?: string} $args The Product slug. + * + * @see src/views/admin/authorize-button.php + * + * @throws InvalidArgumentException + */ + public function render( array $args = [] ): void { + global $pagenow; + + $slug = $args['slug'] ?? ''; + + if ( empty ( $slug ) ) { + throw new InvalidArgumentException( __( 'The Product slug cannot be empty', '%TEXTDOMAIN%' ) ); + } + + $this->auth_url = $this->auth_url_manager->get( $slug ); + + if ( ! $this->auth_url ) { + return; + } + + $authenticated = false; + $target = '_blank'; + $link_text = __( 'Connect', '%TEXTDOMAIN%' ); + $url = $this->build_auth_url(); + $classes = [ + 'uplink-authorize', + 'not-authorized', + ]; + + if ( ! $this->authorizer->can_auth() ) { + $target = '_self'; + $link_text = __( 'Contact your network administrator to connect', '%TEXTDOMAIN%' ); + $url = get_admin_url( get_current_blog_id(), 'network/' ); + } elseif ( $this->token_manager->get() ) { + $authenticated = true; + $target = '_self'; + $link_text = __( 'Disconnect', '%TEXTDOMAIN%' ); + $url = wp_nonce_url( add_query_arg( [ Disconnect_Controller::ARG => true ], get_admin_url( get_current_blog_id() ) ), Disconnect_Controller::ARG ); + $classes[1] = 'authorized'; + } + + $hook_prefix = Config::get_hook_prefix(); + + /** + * Filter the link text. + * + * @param string $link_text The current link text. + * @param bool $authenticated Whether they are authenticated. + * @param string|null $pagenow The value of WordPress's pagenow. + */ + $link_text = apply_filters( + "stellarwp/uplink/$hook_prefix/view/authorize_button/link_text", + $link_text, + $authenticated, + $pagenow + ); + + /** + * Filter the hyperlink url. + * + * @param string $url The current hyperlink url. + * @param bool $authenticated Whether they are authenticated. + * @param string|null $pagenow The value of WordPress's pagenow. + */ + $url = apply_filters( + "stellarwp/uplink/$hook_prefix/view/authorize_button/url", + $url, + $authenticated, + $pagenow + ); + + /** + * Filter the link target. + * + * @param string $target The current link target. + * @param bool $authenticated Whether they are authenticated. + * @param string|null $pagenow The value of WordPress's pagenow. + */ + $target = apply_filters( + "stellarwp/uplink/$hook_prefix/view/authorize_button/target", + $target, + $authenticated, + $pagenow + ); + + /** + * Filter the HTML wrapper tag. + * + * @param string $tag The HTML tag to use for the wrapper. + * @param bool $authenticated Whether they are authenticated. + * @param string|null $pagenow The value of WordPress's pagenow. + */ + $tag = apply_filters( + "stellarwp/uplink/$hook_prefix/view/authorize_button/tag", + 'div', + $authenticated, + $pagenow + ); + + /** + * Filter the CSS classes + * + * @param array $classes An array of CSS classes. + * @param bool $authenticated Whether they are authenticated. + * @param string|null $pagenow The value of WordPress's pagenow. + */ + $classes = (array) apply_filters( + "stellarwp/uplink/$hook_prefix/view/authorize_button/classes", + $classes, + $authenticated, + $pagenow + ); + + echo $this->view->render( self::VIEW, [ + 'link_text' => $link_text, + 'url' => $url, + 'target' => $target, + 'tag' => $tag, + 'classes' => $this->classes( $classes ), + ] ); + } + + /** + * We assume this button is only displayed within wp-admin, + * + * Build the callback URL with the current URL the user is on. + */ + private function build_auth_url(): string { + global $pagenow; + + if ( empty( $pagenow ) ) { + return ''; + } + + $url = add_query_arg( $_GET, admin_url( $pagenow ) ); + + return sprintf( '%s?%s', + $this->auth_url, + http_build_query( [ + 'uplink_callback' => $this->nonce->create_url( $url ), + ] ) + ); + } + +} diff --git a/src/Uplink/Components/Controller.php b/src/Uplink/Components/Controller.php new file mode 100644 index 00000000..e5538e05 --- /dev/null +++ b/src/Uplink/Components/Controller.php @@ -0,0 +1,51 @@ +view = $view; + } + + /** + * Format an array of CSS classes into a string. + * + * @param array $classes + * + * @return string + */ + protected function classes( array $classes ): string { + if ( ! $classes ) { + return ''; + } + + $classes = array_unique( array_map( 'sanitize_html_class', array_filter( $classes ) ) ); + + return implode( ' ', $classes ); + } + +} diff --git a/src/Uplink/Config.php b/src/Uplink/Config.php index 4769c203..dcaaa2ec 100644 --- a/src/Uplink/Config.php +++ b/src/Uplink/Config.php @@ -2,10 +2,17 @@ namespace StellarWP\Uplink; +use InvalidArgumentException; +use RuntimeException; use StellarWP\ContainerContract\ContainerInterface; +use StellarWP\Uplink\Auth\Token\Contracts\Token_Manager; +use StellarWP\Uplink\Utils\Sanitize; class Config { + public const TOKEN_OPTION_NAME = 'uplink.token_prefix'; + + /** * Container object. * @@ -27,15 +34,17 @@ class Config { /** * Get the container. * - * @return ContainerInterface - *@throws \RuntimeException - * * @since 1.0.0 * + * @throws RuntimeException + * + * @return ContainerInterface */ public static function get_container() { if ( self::$container === null ) { - throw new \RuntimeException( 'You must provide a container via StellarWP\Uplink\Config::set_container() before attempting to fetch it.' ); + throw new RuntimeException( + __( 'You must provide a container via StellarWP\Uplink\Config::set_container() before attempting to fetch it.', '%TEXTDOMAIN%' ) + ); } return self::$container; @@ -48,9 +57,11 @@ public static function get_container() { * * @return string */ - public static function get_hook_prefix() { + public static function get_hook_prefix(): string { if ( self::$hook_prefix === null ) { - throw new \RuntimeException( 'You must provide a hook prefix via StellarWP\Uplink\Config::set_hook_prefix() before attempting to fetch it.' ); + throw new RuntimeException( + __( 'You must provide a hook prefix via StellarWP\Uplink\Config::set_hook_prefix() before attempting to fetch it.', '%TEXTDOMAIN%' ) + ); } return static::$hook_prefix; @@ -63,9 +74,11 @@ public static function get_hook_prefix() { * * @return string */ - public static function get_hook_prefix_underscored() { + public static function get_hook_prefix_underscored(): string { if ( self::$hook_prefix === null ) { - throw new \RuntimeException( 'You must provide a hook prefix via StellarWP\Uplink\Config::set_hook_prefix() before attempting to fetch it.' ); + throw new RuntimeException( + __( 'You must provide a hook prefix via StellarWP\Uplink\Config::set_hook_prefix() before attempting to fetch it.', '%TEXTDOMAIN%' ) + ); } return strtolower( str_replace( '-', '_', sanitize_title( static::$hook_prefix ) ) ); @@ -78,7 +91,7 @@ public static function get_hook_prefix_underscored() { * * @return bool */ - public static function has_container() { + public static function has_container(): bool { return self::$container !== null; } @@ -89,20 +102,24 @@ public static function has_container() { * * @return void */ - public static function reset() { + public static function reset(): void { static::$hook_prefix = ''; + + if ( self::has_container() ) { + self::$container->singleton( self::TOKEN_OPTION_NAME, null ); + } } /** * Set the container object. * + * @since 1.0.0 + * * @param ContainerInterface $container Container object. * * @return void - *@since 1.0.0 - * */ - public static function set_container( ContainerInterface $container ) { + public static function set_container( ContainerInterface $container ): void { self::$container = $container; } @@ -115,7 +132,46 @@ public static function set_container( ContainerInterface $container ) { * * @return void */ - public static function set_hook_prefix( string $prefix ) { + public static function set_hook_prefix( string $prefix ): void { static::$hook_prefix = $prefix; } + + /** + * Sets a token options table prefix for storing an origin's authorization token. + * + * This should be the same across all of your products. + * + * @since 1.3.0 + * + * @param string $prefix + * + * @throws RuntimeException|InvalidArgumentException + * + * @return void + */ + public static function set_token_auth_prefix( string $prefix ): void { + if ( ! self::has_container() ) { + throw new RuntimeException( + __( 'You must set a container with StellarWP\Uplink\Config::set_container() before setting a token auth prefix.', '%TEXTDOMAIN%' ) + ); + } + + $prefix = Sanitize::sanitize_title_with_hyphens( rtrim( $prefix, '_' ) ); + $key = sprintf( '%s_%s', $prefix, Token_Manager::TOKEN_SUFFIX ); + + // The option_name column in wp_options is a varchar(191) + $max_length = 191; + + if ( strlen( $key ) > $max_length ) { + throw new InvalidArgumentException( + sprintf( + __( 'The token auth prefix must be at most %d characters, including a trailing hyphen.', '%TEXTDOMAIN%' ), + absint( $max_length - strlen( Token_Manager::TOKEN_SUFFIX ) ) + ) + ); + } + + self::get_container()->singleton( self::TOKEN_OPTION_NAME, $key ); + } + } diff --git a/src/Uplink/Notice/Notice.php b/src/Uplink/Notice/Notice.php new file mode 100644 index 00000000..303e0d87 --- /dev/null +++ b/src/Uplink/Notice/Notice.php @@ -0,0 +1,100 @@ +type = $type; + $this->message = $message; + $this->dismissible = $dismissible; + $this->alt = $alt; + $this->large = $large; + } + + /** + * @return array{type: string, message: string, dismissible: bool, alt: bool, large: bool} + */ + public function toArray(): array { + return get_object_vars( $this ); + } + +} diff --git a/src/Uplink/Notice/Notice_Controller.php b/src/Uplink/Notice/Notice_Controller.php new file mode 100644 index 00000000..4d745217 --- /dev/null +++ b/src/Uplink/Notice/Notice_Controller.php @@ -0,0 +1,42 @@ +view->render( self::VIEW, [ + 'message' => $args['message'], + 'classes' => $this->classes( $classes ) + ] ); + } + +} diff --git a/src/Uplink/Notice/Notice_Handler.php b/src/Uplink/Notice/Notice_Handler.php new file mode 100644 index 00000000..3d8b122b --- /dev/null +++ b/src/Uplink/Notice/Notice_Handler.php @@ -0,0 +1,89 @@ +notices = $this->all(); + $this->controller = $controller; + } + + /** + * Add a notice to display. + * + * @param Notice $notice + * + * @return void + */ + public function add( Notice $notice ): void { + $this->notices = array_merge( $this->all(), [ $notice ] ); + $this->save(); + } + + /** + * Display all notices and then clear them. + * + * @action admin_notices + * + * @return void + */ + public function display(): void { + if ( count( $this->notices ) <= 0 ) { + return; + } + + foreach ( $this->notices as $notice ) { + $this->controller->render( $notice->toArray() ); + } + + $this->clear(); + } + + /** + * Get all notices. + * + * @return Notice[] + */ + private function all(): array { + return array_filter( (array) get_transient( self::TRANSIENT ) ); + } + + /** + * Save the existing state of notices. + * + * @return bool + */ + private function save(): bool { + return set_transient( self::TRANSIENT, $this->notices, 300 ); + } + + /** + * Clear all notices. + * + * @return bool + */ + private function clear(): bool { + return delete_transient( self::TRANSIENT ); + } + +} diff --git a/src/Uplink/Notice/Provider.php b/src/Uplink/Notice/Provider.php new file mode 100644 index 00000000..8c603a58 --- /dev/null +++ b/src/Uplink/Notice/Provider.php @@ -0,0 +1,21 @@ +container->singleton( Notice_Controller::class, Notice_Controller::class ); + $this->container->singleton( Notice_Handler::class, static function ( $c ): Notice_Handler { + return new Notice_Handler( $c->get( Notice_Controller::class ) ); + } ); + + add_action( 'admin_notices', [ $this->container->get( Notice_Handler::class ), 'display' ], 12, 0 ); + } + +} diff --git a/src/Uplink/Pipeline/Pipeline.php b/src/Uplink/Pipeline/Pipeline.php new file mode 100644 index 00000000..beed20ee --- /dev/null +++ b/src/Uplink/Pipeline/Pipeline.php @@ -0,0 +1,272 @@ +container = $container; + } + + /** + * Set the object being sent through the pipeline. + * + * @param mixed $passable + * + * @return $this + */ + public function send( $passable ): Pipeline { + $this->passable = $passable; + + return $this; + } + + /** + * Set the array of pipes. + * + * @param array|mixed $pipes + * + * @return $this + */ + public function through( $pipes ): Pipeline { + $this->pipes = is_array( $pipes ) ? $pipes : func_get_args(); + + return $this; + } + + /** + * Push additional pipes onto the pipeline. + * + * @param array|mixed $pipes + * + * @return $this + */ + public function pipe( $pipes ): Pipeline { + array_push( $this->pipes, ...( is_array( $pipes ) ? $pipes : func_get_args() ) ); + + return $this; + } + + /** + * Set the method to call on the pipes. + * + * @param string $method + * + * @return $this + */ + public function via( string $method ): Pipeline { + $this->method = $method; + + return $this; + } + + /** + * Run the pipeline with a final destination callback. + * + * @param \Closure $destination + * + * @return mixed + */ + public function then( Closure $destination ) { + $pipeline = array_reduce( + array_reverse( $this->pipes() ), $this->carry(), $this->prepareDestination( $destination ) + ); + + return $pipeline( $this->passable ); + } + + /** + * Run the pipeline and return the result. + * + * @return mixed + */ + public function thenReturn() { + return $this->then( function ( $passable ) { + return $passable; + } ); + } + + /** + * Get the final piece of the Closure onion. + * + * @param \Closure $destination + * + * @return \Closure + */ + protected function prepareDestination( Closure $destination ): Closure { + return function ( $passable ) use ( $destination ) { + try { + return $destination( $passable ); + } catch ( Throwable $e ) { + return $this->handleException( $passable, $e ); + } + }; + } + + /** + * Get a Closure that represents a slice of the application onion. + * + * @return \Closure + */ + protected function carry(): Closure { + return function ( $stack, $pipe ) { + return function ( $passable ) use ( $stack, $pipe ) { + try { + if ( is_callable( $pipe ) ) { + // If the pipe is a callable, then we will call it directly, but otherwise we + // will resolve the pipes out of the dependency container and call it with + // the appropriate method and arguments, returning the results back out. + return $pipe( $passable, $stack ); + } elseif ( ! is_object( $pipe ) ) { + [ $name, $parameters ] = $this->parsePipeString( $pipe ); + + // If the pipe is a string we will parse the string and resolve the class out + // of the dependency injection container. We can then build a callable and + // execute the pipe function giving in the parameters that are required. + $pipe = $this->getContainer()->get( $name ); + + $parameters = array_merge( [ $passable, $stack ], $parameters ); + } else { + // If the pipe is already an object we'll just make a callable and pass it to + // the pipe as-is. There is no need to do any extra parsing and formatting + // since the object we're given was already a fully instantiated object. + $parameters = [ $passable, $stack ]; + } + + $carry = method_exists( $pipe, $this->method ) + ? $pipe->{$this->method}( ...$parameters ) + : $pipe( ...$parameters ); + + return $this->handleCarry( $carry ); + } catch ( Throwable $e ) { + return $this->handleException( $passable, $e ); + } + }; + }; + } + + /** + * Parse full pipe string to get name and parameters. + * + * @param string $pipe + * + * @return array + */ + protected function parsePipeString( string $pipe ): array { + [ $name, $parameters ] = array_pad( explode( ':', $pipe, 2 ), 2, [] ); + + if ( is_string( $parameters ) ) { + $parameters = explode( ',', $parameters ); + } + + return [ $name, $parameters ]; + } + + /** + * Get the array of configured pipes. + * + * @return array + */ + protected function pipes(): array { + return $this->pipes; + } + + /** + * Get the container instance. + * + * @throws \RuntimeException + * + * @return ContainerInterface + */ + protected function getContainer(): ?ContainerInterface { + if ( ! $this->container ) { + throw new RuntimeException( 'A container instance has not been passed to the Pipeline.' ); + } + + return $this->container; + } + + /** + * Set the container instance. + * + * @param ContainerInterface $container + * + * @return $this + */ + public function setContainer( ContainerInterface $container ): Pipeline { + $this->container = $container; + + return $this; + } + + /** + * Handle the value returned from each pipe before passing it to the next. + * + * @param mixed $carry + * + * @return mixed + */ + protected function handleCarry( $carry ) { + return $carry; + } + + /** + * Handle the given exception. + * + * @param mixed $passable + * @param \Throwable $e + * + * @throws \Throwable + * + * @return mixed + */ + protected function handleException( $passable, Throwable $e ) { + throw $e; + } + +} diff --git a/src/Uplink/Register.php b/src/Uplink/Register.php index ccff8868..a8eb8bcd 100644 --- a/src/Uplink/Register.php +++ b/src/Uplink/Register.php @@ -11,8 +11,8 @@ class Register { * * @since 1.0.0 * - * @param string $name Resource name. * @param string $slug Resource slug. + * @param string $name Resource name. * @param string $version Resource version. * @param string $path Resource path to bootstrap file. * @param string $class Resource class. @@ -20,8 +20,8 @@ class Register { * * @return Resources\Resource */ - public static function plugin( $name, $slug, $version, $path, $class, $license_class = null ) { - return Resources\Plugin::register( $name, $slug, $version, $path, $class, $license_class ); + public static function plugin( $slug, $name, $version, $path, $class, $license_class = null ) { + return Resources\Plugin::register( $slug, $name, $version, $path, $class, $license_class ); } /** @@ -29,8 +29,8 @@ public static function plugin( $name, $slug, $version, $path, $class, $license_c * * @since 1.0.0 * - * @param string $name Resource name. * @param string $slug Resource slug. + * @param string $name Resource name. * @param string $version Resource version. * @param string $path Resource path to bootstrap file. * @param string $class Resource class. @@ -38,7 +38,7 @@ public static function plugin( $name, $slug, $version, $path, $class, $license_c * * @return Resources\Resource */ - public static function service( $name, $slug, $version, $path, $class, $license_class = null ) { - return Resources\Service::register( $name, $slug, $version, $path, $class, $license_class ); + public static function service( $slug, $name, $version, $path, $class, $license_class = null ) { + return Resources\Service::register( $slug, $name, $version, $path, $class, $license_class ); } } diff --git a/src/Uplink/Resources/Collection.php b/src/Uplink/Resources/Collection.php index afefefd8..fb5b7331 100644 --- a/src/Uplink/Resources/Collection.php +++ b/src/Uplink/Resources/Collection.php @@ -2,11 +2,15 @@ namespace StellarWP\Uplink\Resources; -class Collection implements \ArrayAccess, \Iterator { +use ArrayAccess; +use Iterator; + +class Collection implements ArrayAccess, Iterator { + /** * Collection of resources. * - * @var array + * @var array */ private $resources = []; @@ -17,7 +21,7 @@ class Collection implements \ArrayAccess, \Iterator { * * @param Resource $resource Resource instance. * - * @return mixed + * @return Resource */ public function add( Resource $resource ) { $this->offsetSet( $resource->get_slug(), $resource ); @@ -26,10 +30,10 @@ public function add( Resource $resource ) { } /** - * @inheritDoc + * @return Resource */ #[\ReturnTypeWillChange] - public function current() { + public function current(): Resource { return current( $this->resources ); } @@ -39,7 +43,7 @@ public function current() { * @since 1.0.0 * * @param string $path Path to filter collection by. - * @param \Iterator $iterator Optional. Iterator to filter. + * @param Iterator $iterator Optional. Iterator to filter. * * @return Filters\Path_FilterIterator */ @@ -53,7 +57,7 @@ public function get_by_path( string $path, $iterator = null ) { * @since 1.0.0 * * @param array $paths Paths to filter collection by. - * @param \Iterator $iterator Optional. Iterator to filter. + * @param Iterator $iterator Optional. Iterator to filter. * * @return Filters\Path_FilterIterator */ @@ -66,7 +70,7 @@ public function get_by_paths( array $paths, $iterator = null ) { * * @since 1.0.0 * - * @param \Iterator $iterator Optional. Iterator to filter. + * @param Iterator $iterator Optional. Iterator to filter. * * @return Filters\Plugin_FilterIterator */ @@ -79,7 +83,7 @@ public function get_plugins( $iterator = null ) { * * @since 1.0.0 * - * @param \Iterator $iterator Optional. Iterator to filter. + * @param Iterator $iterator Optional. Iterator to filter. * * @return Filters\Service_FilterIterator */ @@ -88,7 +92,7 @@ public function get_services( $iterator = null ) { } /** - * @inheritDoc + * @return array-key|null */ #[\ReturnTypeWillChange] public function key() { @@ -110,11 +114,11 @@ public function offsetExists( $offset ): bool { } /** - * @inheritDoc + * @return Resource|null */ #[\ReturnTypeWillChange] - public function offsetGet( $offset ) { - return $this->resources[ $offset ]; + public function offsetGet( $offset ): ?Resource { + return $this->resources[ $offset ] ?? null; } /** diff --git a/src/Uplink/Resources/License.php b/src/Uplink/Resources/License.php index 727d90aa..ec14f4eb 100644 --- a/src/Uplink/Resources/License.php +++ b/src/Uplink/Resources/License.php @@ -356,13 +356,25 @@ public function is_validation_expired() { * @return bool */ public function set_key( string $key, string $type = 'local' ): bool { + $key = Utils\Sanitize::key( $key ); + $this->key = $key; if ( 'network' === $type && is_multisite() ) { - return update_network_option( 0, $this->get_key_option_name(), Utils\Sanitize::key( $key ) ); + // WordPress would otherwise return false if the keys already match. + if ( $this->get_key_from_network_option() === $key ) { + return true; + } + + return update_network_option( 0, $this->get_key_option_name(), $key ); + } + + // WordPress would otherwise return false if the keys already match. + if ( $this->get_key_from_option() === $key ) { + return true; } - return update_option( $this->get_key_option_name(), Utils\Sanitize::key( $key ) ); + return update_option( $this->get_key_option_name(), $key ); } /** diff --git a/src/Uplink/Resources/Plugin.php b/src/Uplink/Resources/Plugin.php index f276f5d2..96457c40 100644 --- a/src/Uplink/Resources/Plugin.php +++ b/src/Uplink/Resources/Plugin.php @@ -11,7 +11,7 @@ class Plugin extends Resource { * * @since 1.0.0 * - * @var \stdClass + * @var \stdClass|null */ protected $update_status; @@ -108,7 +108,7 @@ protected function get_version_from_response( $response ): string { * * @return mixed */ - public function get_update_status( $force_fetch = false) { + public function get_update_status( $force_fetch = false ) { if ( ! $force_fetch ) { $this->update_status = get_option( $this->get_update_status_option_name(), null ); } diff --git a/src/Uplink/Traits/With_Debugging.php b/src/Uplink/Traits/With_Debugging.php new file mode 100644 index 00000000..babe35b3 --- /dev/null +++ b/src/Uplink/Traits/With_Debugging.php @@ -0,0 +1,16 @@ +bind( ContainerInterface::class, $container ); + $container->singleton( View\Provider::class, View\Provider::class ); $container->singleton( API\Client::class, API\Client::class ); + $container->singleton( API\V3\Provider::class, API\V3\Provider::class ); $container->singleton( Resources\Collection::class, Resources\Collection::class ); $container->singleton( Site\Data::class, Site\Data::class ); + $container->singleton( Notice\Provider::class, Notice\Provider::class ); $container->singleton( Admin\Provider::class, Admin\Provider::class ); + $container->singleton( Auth\Provider::class, Auth\Provider::class ); if ( static::is_enabled() ) { + $container->get( View\Provider::class )->register(); + $container->get( API\V3\Provider::class )->register(); + $container->get( Notice\Provider::class )->register(); $container->get( Admin\Provider::class )->register(); + + if ( $container->has( Config::TOKEN_OPTION_NAME ) ) { + $container->get( Auth\Provider::class )->register(); + } } + + require_once __DIR__ . '/functions.php'; } /** - * Returns whether or not licensing validation is disabled. + * Returns whether licensing validation is disabled. * * @since 1.0.0 * @@ -43,7 +62,7 @@ public static function is_disabled() : bool { } /** - * Returns whether or not licensing validation is enabled. + * Returns whether licensing validation is enabled. * * @since 1.0.0 * diff --git a/src/Uplink/Utils/Checks.php b/src/Uplink/Utils/Checks.php index 17cd95d2..e03a5eda 100644 --- a/src/Uplink/Utils/Checks.php +++ b/src/Uplink/Utils/Checks.php @@ -54,4 +54,20 @@ public static function is_truthy( $var ) { return (bool) $var; } + /** + * String Starts With PHP80 polyfill. + * + * @param string $haystack The string to search in. + * @param string $needle The substring to search for in the haystack. + * + * @return bool Returns true if haystack begins with needle, false otherwise. + */ + public static function str_starts_with( string $haystack, string $needle ): bool { + if ( function_exists( 'str_starts_with' ) ) { + return str_starts_with( $haystack, $needle ); + } + + return 0 === strncmp( $haystack, $needle, strlen( $needle ) ); + } + } diff --git a/src/Uplink/Utils/Sanitize.php b/src/Uplink/Utils/Sanitize.php index 47dbe61a..7a58947a 100644 --- a/src/Uplink/Utils/Sanitize.php +++ b/src/Uplink/Utils/Sanitize.php @@ -15,4 +15,23 @@ class Sanitize { public static function key( $key ) { return str_replace( [ '`', '"', "'" ], '', $key ); } + + /** + * Sanitizes a title, replacing whitespace and a few other characters with hyphens. + * + * Limits the output to alphanumeric characters, underscore (_) and dash (-). + * Whitespace becomes a hyphen. + * + * @since 1.3.0 + * + * @param string $title The title to be sanitized. + * @param string $context Optional. The operation for which the string is sanitized. + * When set to 'save', additional entities are converted to hyphens + * or stripped entirely. Default 'display'. + * @return string The sanitized title. + */ + public static function sanitize_title_with_hyphens( string $title, string $context = 'save' ): string { + return str_replace( '-', '_', sanitize_title_with_dashes( $title, '', $context ) ); + } + } diff --git a/src/Uplink/View/Contracts/View.php b/src/Uplink/View/Contracts/View.php new file mode 100644 index 00000000..adbc2dac --- /dev/null +++ b/src/Uplink/View/Contracts/View.php @@ -0,0 +1,25 @@ +container->singleton( + WordPress_View::class, + new WordPress_View( __DIR__ . '/../../views' ) + ); + + $this->container->bind( View::class, $this->container->get( WordPress_View::class ) ); + } +} diff --git a/src/Uplink/View/WordPress_View.php b/src/Uplink/View/WordPress_View.php new file mode 100644 index 00000000..5432a3cf --- /dev/null +++ b/src/Uplink/View/WordPress_View.php @@ -0,0 +1,97 @@ +directory = trailingslashit( realpath( $directory ) ); + $this->extension = $extension; + } + + /** + * Renders a view and returns it as a string to be echoed. + * + * @example If the server path is /app/views, and you wish to load /app/views/admin/notice.php, + * pass `admin/notice` as the view name. + * + * @param string $name The relative path/name of the view file without extension. + * + * @param mixed[] $args Arguments to be extracted and passed to the view. + * + * @throws FileNotFoundException If the view file cannot be found. + * + * @return string + */ + public function render( string $name, array $args = [] ): string { + $file = $this->get_path( $name ); + + try { + $level = ob_get_level(); + ob_start(); + + extract( $args ); + include $file; + + return (string) ob_get_clean(); + } catch ( Throwable $e ) { + while ( ob_get_level() > $level ) { + ob_end_clean(); + } + + throw $e; + } + } + + /** + * Get the absolute server path to a view file. + * + * @param string $name The relative view path/name, e.g. `admin/notice`. + * + * @throws FileNotFoundException If the view file cannot be found. + * + * @return string The absolute path to the view file. + */ + private function get_path( string $name ): string { + $file = $this->directory . $name . $this->extension; + $path = realpath( $file ); + + if( $path === false ) { + throw new FileNotFoundException( + sprintf( __( 'View file "%s" not found or not readable.', '%TEXTDOMAIN%' ), $file ) + ); + } + + return $path; + } + +} diff --git a/src/Uplink/functions.php b/src/Uplink/functions.php new file mode 100644 index 00000000..8b1cbb2a --- /dev/null +++ b/src/Uplink/functions.php @@ -0,0 +1,66 @@ +get( Authorize_Button_Controller::class ) + ->render( [ 'slug' => $slug ] ); + } catch ( Throwable $e ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + error_log( "Unable to render authorize button: {$e->getMessage()} {$e->getFile()}:{$e->getLine()} {$e->getTraceAsString()}" ); + } + } +} + +/** + * Get the stored authorization token. + * + * @return string|null + */ +function get_authorization_token(): ?string { + try { + return Config::get_container()->get( Token_Manager::class )->get(); + } catch ( Throwable $e ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + error_log( "Error occurred when fetching token: {$e->getMessage()} {$e->getFile()}:{$e->getLine()} {$e->getTraceAsString()}" ); + } + + return null; + } +} + +/** + * Manually check if a license is authorized. + * + * @param string $license The license key. + * @param string $token The stored token. + * @param string $domain The user's domain. + * + * @return bool + */ +function is_authorized( string $license, string $token, string $domain ): bool { + try { + return Config::get_container() + ->get( Token_Authorizer::class ) + ->is_authorized( $license, $token, $domain ); + } catch ( Throwable $e ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + error_log( "An Authorization error occurred: {$e->getMessage()} {$e->getFile()}:{$e->getLine()} {$e->getTraceAsString()}" ); + } + + return false; + } +} diff --git a/src/views/admin/authorize-button.php b/src/views/admin/authorize-button.php new file mode 100644 index 00000000..0f5983c1 --- /dev/null +++ b/src/views/admin/authorize-button.php @@ -0,0 +1,26 @@ + + +< class="uplink-authorize-container"> + + > + + +> + diff --git a/src/views/admin/index.php b/src/views/admin/index.php new file mode 100644 index 00000000..9170000d --- /dev/null +++ b/src/views/admin/index.php @@ -0,0 +1 @@ + +
+

+
diff --git a/src/views/index.php b/src/views/index.php new file mode 100644 index 00000000..9170000d --- /dev/null +++ b/src/views/index.php @@ -0,0 +1 @@ +container = Config::get_container(); } + } diff --git a/tests/_support/_generated/MuwpunitTesterActions.php b/tests/_support/_generated/MuwpunitTesterActions.php deleted file mode 100644 index ee636761..00000000 --- a/tests/_support/_generated/MuwpunitTesterActions.php +++ /dev/null @@ -1,1171 +0,0 @@ -getPluginsFolder(); - * $hello = $this->getPluginsFolder('hello.php'); - * ``` - * - * @param string $path A relative path to append to te plugins directory absolute path. - * - * @return string The absolute path to the `pluginsFolder` path or the same with a relative path appended if `$path` - * is provided. - * - * @throws ModuleConfigException If the path to the plugins folder does not exist. - * @see \Codeception\Module\WPLoader::getPluginsFolder() - */ - public function getPluginsFolder($path = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getPluginsFolder', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Accessor method to get the object storing the factories for things. - * This methods gives access to the same factories provided by the - * [Core test suite](https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/). - * - * @return FactoryStore A factory store, proxy to get hold of the Core suite object - * factories. - * - * @example - * ```php - * $postId = $I->factory()->post->create(); - * $userId = $I->factory()->user->create(['role' => 'administrator']); - * ``` - * - * @link https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/ - * @see \Codeception\Module\WPLoader::factory() - */ - public function factory() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('factory', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the absolute path to the WordPress content directory. - * - * @example - * ```php - * $content = $this->getContentFolder(); - * $themes = $this->getContentFolder('themes'); - * $twentytwenty = $this->getContentFolder('themes/twentytwenty'); - * ``` - * - * @param string $path An optional path to append to the content directory absolute path. - * - * @return string The content directory absolute path, or a path in it. - * - * @throws ModuleConfigException If the path to the content directory cannot be resolved. - * @see \Codeception\Module\WPLoader::getContentFolder() - */ - public function getContentFolder($path = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getContentFolder', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Starts the debug of all WordPress filters and actions. - * - * The method hook on `all` filters and actions to debug their value. - * - * @example - * ```php - * // Start debugging all WordPress filters and action final and initial values. - * $this->startWpFiltersDebug(); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters and action final and initial values. - * $this->stopWpFiltersDebug(); - * ``` - * - * @param callable|null $format A callback function to format the arguments debug output; the callback will receive - * the array of arguments as input. - * - * @return void - * @see \Codeception\Module\WPLoader::startWpFiltersDebug() - */ - public function startWpFiltersDebug(?callable $format = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('startWpFiltersDebug', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Stops the debug of all WordPress filters and actions. - * - * @example - * ```php - * // Start debugging all WordPress filters and action final and initial values. - * $this->startWpFiltersDebug(); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters and action final and initial values. - * $this->stopWpFiltersDebug(); - * ``` - * - * @return void - * @see \Codeception\Module\WPLoader::stopWpFiltersDebug() - */ - public function stopWpFiltersDebug() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('stopWpFiltersDebug', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress filter initial call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress filters initial value. - * add_filter('all', [$this,'debugWpFilterInitial']); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters initial value. - * remove_filter('all', [$this,'debugWpFilterInitial']); - * ``` - * - * @param mixed ...$args The filter call arguments. - * - * @return mixed The filter input value, unchanged. - * @see \Codeception\Module\WPLoader::debugWpFilterInitial() - */ - public function debugWpFilterInitial($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpFilterInitial', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress filter final call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress filters final value. - * add_filter('all', [$this,'debugWpFilterFinal']); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters final value. - * remove_filter('all', [$this,'debugWpFilterFinal']); - * ``` - * - * @param mixed ...$args The filter call arguments. - * - * @return mixed The filter input value, unchanged. - * @see \Codeception\Module\WPLoader::debugWpFilterFinal() - */ - public function debugWpFilterFinal($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpFilterFinal', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress action initial call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress actions initial value. - * add_action('all', [$this,'debugWpActionInitial']); - * - * // Run some code firing actions and debug them. - * - * // Stop debugging all WordPress actions initial value. - * remove_action('all', [$this,'debugWpActionInitial']); - * ``` - * - * @param mixed ...$args The action call arguments. - * - * @return void - * @see \Codeception\Module\WPLoader::debugWpActionInitial() - */ - public function debugWpActionInitial($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpActionInitial', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress action final call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress actions final value. - * add_action('all', [$this,'debugWpActionFinal']); - * - * // Run some code firing actions and debug them. - * - * // Stop debugging all WordPress actions final value. - * remove_action('all', [$this,'debugWpActionFinal']); - * ``` - * - * @param mixed ...$args The action call arguments. - * - * @return void - * @see \Codeception\Module\WPLoader::debugWpActionFinal() - */ - public function debugWpActionFinal($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpActionFinal', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made during the test. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_cache_delete('page-posts', 'acme'); - * $pagePosts = $plugin->getPagePosts(); - * $I->assertQueries('Queries should be made to set the cache.') - * ``` - * - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueries() - */ - public function assertQueries($message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $posts = $this->factory()->post->create_many(3); - * wp_cache_set('page-posts', $posts, 'acme'); - * $pagePosts = $plugin->getPagePosts(); - * $I->assertNotQueries('Queries should not be made if the cache is set.') - * ``` - * - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueries() - */ - public function assertNotQueries($message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries have been made. - * - * @example - * ```php - * $posts = $this->factory()->post->create_many(3); - * $cachedUsers = $this->factory()->user->create_many(2); - * $nonCachedUsers = $this->factory()->user->create_many(2); - * foreach($cachedUsers as $userId){ - * wp_cache_set('page-posts-for-user-' . $userId, $posts, 'acme'); - * } - * // Run the same query as different users - * foreach(array_merge($cachedUsers, $nonCachedUsers) as $userId){ - * $pagePosts = $plugin->getPagePostsForUser($userId); - * } - * $I->assertCountQueries(2, 'A query should be made for each user missing cached posts.') - * ``` - * - * @param int $n The expected number of queries. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertCountQueries() - */ - public function assertCountQueries($n, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertCountQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least a query starting with the specified statement was made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_cache_flush(); - * cached_get_posts($args); - * $I->assertQueriesByStatement('SELECT'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatement() - */ - public function assertQueriesByStatement($statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query has been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $options = new Acme\Options(); - * $options->update('showAds', false); - * $I->assertQueriesByMethod('Acme\Options', 'update'); - * ``` - * - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByMethod() - */ - public function assertQueriesByMethod($class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->set('title', 'Peter Pan', $deferred = true); - * $this->assertNotQueriesByStatement('INSERT', 'Deferred write should happen on __destruct'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatement() - */ - public function assertNotQueriesByStatement($statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries starting with the specified statement were made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->set('title', 'Peter Pan', $deferred = true); - * $repository->where('ID', 89)->set('title', 'Moby-dick', $deferred = true); - * $repository->where('ID', 2389)->set('title', 'The call of the wild', $deferred = false); - * $this->assertQueriesCountByStatement(1, 'INSERT', 'Deferred write should happen on __destruct'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatement() - */ - public function assertQueriesCountByStatement($n, $statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $options = new Acme\Options(); - * $options->update('adsSource', 'not-a-real-url.org'); - * $I->assertNotQueriesByMethod('Acme\Options', 'update'); - * ``` - * - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByMethod() - */ - public function assertNotQueriesByMethod($class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->commit('title', 'Peter Pan'); - * $repository->where('ID', 89)->commit('title', 'Moby-dick'); - * $repository->where('ID', 2389)->commit('title', 'The call of the wild'); - * $this->assertQueriesCountByMethod(3, 'Acme\BookRepository', 'commit'); - * ``` - * @param int $n The expected number of queries. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByMethod() - */ - public function assertQueriesCountByMethod($n, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * acme_clean_queue(); - * $this->assertQueriesByFunction('acme_clean_queue'); - * ``` - * - * @param string $function The fully qualified name of the function to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByFunction() - */ - public function assertQueriesByFunction($function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $this->assertEmpty(Acme\get_orphaned_posts()); - * Acme\delete_orphaned_posts(); - * $this->assertNotQueriesByFunction('Acme\delete_orphaned_posts'); - * ``` - * - * @param string $function The fully qualified name of the function to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByFunction() - */ - public function assertNotQueriesByFunction($function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $this->assertCount(3, Acme\get_orphaned_posts()); - * Acme\delete_orphaned_posts(); - * $this->assertQueriesCountByFunction(3, 'Acme\delete_orphaned_posts'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $function The function to check the queries for. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByFunction() - */ - public function assertQueriesCountByFunction($n, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * $this->assertQueriesByStatementAndMethod('UPDATE', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndMethod() - */ - public function assertQueriesByStatementAndMethod($statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * $this->assertQueriesByStatementAndMethod('INSERT', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndMethod() - */ - public function assertNotQueriesByStatementAndMethod($statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * Acme\BookRepository::new(['title' => 'Moby-Dick'])->commit(); - * Acme\BookRepository::new(['title' => 'The Call of the Wild'])->commit(); - * $this->assertQueriesCountByStatementAndMethod(3, 'INSERT', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndMethod() - */ - public function assertQueriesCountByStatementAndMethod($n, $statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['post_type' => 'book', 'post_title' => 'Alice in Wonderland']); - * $this->assertQueriesByStatementAndFunction('INSERT', 'wp_insert_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The fully qualified function name. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndFunction() - */ - public function assertQueriesByStatementAndFunction($statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['ID' => $bookId, 'post_title' => 'The Call of the Wild']); - * $this->assertNotQueriesByStatementAndFunction('INSERT', 'wp_insert_post'); - * $this->assertQueriesByStatementAndFunction('UPDATE', 'wp_insert_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The name of the function to check the assertions for. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndFunction() - */ - public function assertNotQueriesByStatementAndFunction($statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['post_type' => 'book', 'post_title' => 'The Call of the Wild']); - * wp_insert_post(['post_type' => 'book', 'post_title' => 'Alice in Wonderland']); - * wp_insert_post(['post_type' => 'book', 'post_title' => 'The Chocolate Factory']); - * $this->assertQueriesCountByStatementAndFunction(3, 'INSERT', 'wp_insert_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The fully-qualified function name. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndFunction() - */ - public function assertQueriesCountByStatementAndFunction($n, $statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookId, 'post_title' => 'New Title']); - * $this->assertQueriesByAction('edit_post'); - * ``` - * - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByAction() - */ - public function assertQueriesByAction($action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookId); - * $this->assertNotQueriesByAction('edit_post'); - * ``` - * - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByAction() - */ - public function assertNotQueriesByAction($action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookOneId, 'post_title' => 'One']); - * wp_update_post(['ID' => $bookTwoId, 'post_title' => 'Two']); - * wp_update_post(['ID' => $bookThreeId, 'post_title' => 'Three']); - * $this->assertQueriesCountByAction(3, 'edit_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByAction() - */ - public function assertQueriesCountByAction($n, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified action containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookId, 'post_title' => 'New']); - * $this->assertQueriesByStatementAndAction('UPDATE', 'edit_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndAction() - */ - public function assertQueriesByStatementAndAction($statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified action containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookId); - * $this->assertNotQueriesByStatementAndAction('DELETE', 'delete_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndAction() - */ - public function assertNotQueriesByStatementAndAction($statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified action containing the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookOneId); - * wp_delete_post($bookTwoId); - * wp_update_post(['ID' => $bookThreeId, 'post_title' => 'New']); - * $this->assertQueriesCountByStatementAndAction(2, 'DELETE', 'delete_post'); - * $this->assertQueriesCountByStatementAndAction(1, 'INSERT', 'edit_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndAction() - */ - public function assertQueriesCountByStatementAndAction($n, $statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookId)->post_title, $bookId); - * $this->assertQueriesByFilter('the_title'); - * ``` - * - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByFilter() - */ - public function assertQueriesByFilter($filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $this->assertNotQueriesByFilter('the_title'); - * ``` - * - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByFilter() - */ - public function assertNotQueriesByFilter($filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookOneId)->post_title, $bookOneId); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $title = apply_filters('the_title', get_post($bookTwoId)->post_title, $bookTwoId); - * $this->assertQueriesCountByFilter(2, 'the_title'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByFilter() - */ - public function assertQueriesCountByFilter($n, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified filter containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookId)->post_title, $bookId); - * $this->assertQueriesByStatementAndFilter('SELECT', 'the_title'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndFilter() - */ - public function assertQueriesByStatementAndFilter($statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified filter containing the specified SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $this->assertNotQueriesByStatementAndFilter('SELECT', 'the_title'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndFilter() - */ - public function assertNotQueriesByStatementAndFilter($statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified filter containing the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * // Warm up the cache. - * $title = apply_filters('the_title', get_post($bookOneId)->post_title, $bookOneId); - * // Cache is warmed up now. - * $title = apply_filters('the_title', get_post($bookTwoId)->post_title, $bookTwoId); - * $title = apply_filters('the_title', get_post($bookThreeId)->post_title, $bookThreeId); - * $this->assertQueriesCountByStatementAndFilter(1, 'SELECT', 'the_title'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndFilter() - */ - public function assertQueriesCountByStatementAndFilter($n, $statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the current number of queries. - * Set-up and tear-down queries performed by the test case are filtered out. - * - * @example - * ```php - * // In a WPTestCase, using the global $wpdb object. - * $queriesCount = $this->queries()->countQueries(); - * // In a WPTestCase, using a custom $wpdb object. - * $queriesCount = $this->queries()->countQueries($customWdbb); - * ``` - * - * @param \wpdb|null $wpdb A specific instance of the `wpdb` class or `null` to use the global one. - * - * @return int The current count of performed queries. - * @see \Codeception\Module\WPQueries::countQueries() - */ - public function countQueries($wpdb = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('countQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the queries currently performed by the global database object or the specified one. - * Set-up and tear-down queries performed by the test case are filtered out. - * - * @example - * ```php - * // In a WPTestCase, using the global $wpdb object. - * $queries = $this->queries()->getQueries(); - * // In a WPTestCase, using a custom $wpdb object. - * $queries = $this->queries()->getQueries($customWdbb); - * ``` - * - * @param null|\wpdb $wpdb A specific instance of the `wpdb` class or `null` to use the global one. - * - * @return array An array of queries. - * @see \Codeception\Module\WPQueries::getQueries() - */ - public function getQueries($wpdb = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getQueries', func_get_args())); - } -} diff --git a/tests/_support/_generated/WpunitTesterActions.php b/tests/_support/_generated/WpunitTesterActions.php deleted file mode 100644 index e2394879..00000000 --- a/tests/_support/_generated/WpunitTesterActions.php +++ /dev/null @@ -1,1171 +0,0 @@ -getPluginsFolder(); - * $hello = $this->getPluginsFolder('hello.php'); - * ``` - * - * @param string $path A relative path to append to te plugins directory absolute path. - * - * @return string The absolute path to the `pluginsFolder` path or the same with a relative path appended if `$path` - * is provided. - * - * @throws ModuleConfigException If the path to the plugins folder does not exist. - * @see \Codeception\Module\WPLoader::getPluginsFolder() - */ - public function getPluginsFolder($path = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getPluginsFolder', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Accessor method to get the object storing the factories for things. - * This methods gives access to the same factories provided by the - * [Core test suite](https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/). - * - * @return FactoryStore A factory store, proxy to get hold of the Core suite object - * factories. - * - * @example - * ```php - * $postId = $I->factory()->post->create(); - * $userId = $I->factory()->user->create(['role' => 'administrator']); - * ``` - * - * @link https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/ - * @see \Codeception\Module\WPLoader::factory() - */ - public function factory() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('factory', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the absolute path to the WordPress content directory. - * - * @example - * ```php - * $content = $this->getContentFolder(); - * $themes = $this->getContentFolder('themes'); - * $twentytwenty = $this->getContentFolder('themes/twentytwenty'); - * ``` - * - * @param string $path An optional path to append to the content directory absolute path. - * - * @return string The content directory absolute path, or a path in it. - * - * @throws ModuleConfigException If the path to the content directory cannot be resolved. - * @see \Codeception\Module\WPLoader::getContentFolder() - */ - public function getContentFolder($path = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getContentFolder', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Starts the debug of all WordPress filters and actions. - * - * The method hook on `all` filters and actions to debug their value. - * - * @example - * ```php - * // Start debugging all WordPress filters and action final and initial values. - * $this->startWpFiltersDebug(); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters and action final and initial values. - * $this->stopWpFiltersDebug(); - * ``` - * - * @param callable|null $format A callback function to format the arguments debug output; the callback will receive - * the array of arguments as input. - * - * @return void - * @see \Codeception\Module\WPLoader::startWpFiltersDebug() - */ - public function startWpFiltersDebug(?callable $format = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('startWpFiltersDebug', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Stops the debug of all WordPress filters and actions. - * - * @example - * ```php - * // Start debugging all WordPress filters and action final and initial values. - * $this->startWpFiltersDebug(); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters and action final and initial values. - * $this->stopWpFiltersDebug(); - * ``` - * - * @return void - * @see \Codeception\Module\WPLoader::stopWpFiltersDebug() - */ - public function stopWpFiltersDebug() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('stopWpFiltersDebug', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress filter initial call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress filters initial value. - * add_filter('all', [$this,'debugWpFilterInitial']); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters initial value. - * remove_filter('all', [$this,'debugWpFilterInitial']); - * ``` - * - * @param mixed ...$args The filter call arguments. - * - * @return mixed The filter input value, unchanged. - * @see \Codeception\Module\WPLoader::debugWpFilterInitial() - */ - public function debugWpFilterInitial($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpFilterInitial', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress filter final call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress filters final value. - * add_filter('all', [$this,'debugWpFilterFinal']); - * - * // Run some code firing filters and debug them. - * - * // Stop debugging all WordPress filters final value. - * remove_filter('all', [$this,'debugWpFilterFinal']); - * ``` - * - * @param mixed ...$args The filter call arguments. - * - * @return mixed The filter input value, unchanged. - * @see \Codeception\Module\WPLoader::debugWpFilterFinal() - */ - public function debugWpFilterFinal($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpFilterFinal', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress action initial call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress actions initial value. - * add_action('all', [$this,'debugWpActionInitial']); - * - * // Run some code firing actions and debug them. - * - * // Stop debugging all WordPress actions initial value. - * remove_action('all', [$this,'debugWpActionInitial']); - * ``` - * - * @param mixed ...$args The action call arguments. - * - * @return void - * @see \Codeception\Module\WPLoader::debugWpActionInitial() - */ - public function debugWpActionInitial($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpActionInitial', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Debugs a single WordPress action final call using Codeception debug functions. - * - * The output will show following the selected output verbosity (`--debug` and `-vvv` CLI options). - * - * @example - * ```php - * // Start debugging all WordPress actions final value. - * add_action('all', [$this,'debugWpActionFinal']); - * - * // Run some code firing actions and debug them. - * - * // Stop debugging all WordPress actions final value. - * remove_action('all', [$this,'debugWpActionFinal']); - * ``` - * - * @param mixed ...$args The action call arguments. - * - * @return void - * @see \Codeception\Module\WPLoader::debugWpActionFinal() - */ - public function debugWpActionFinal($args = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('debugWpActionFinal', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made during the test. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_cache_delete('page-posts', 'acme'); - * $pagePosts = $plugin->getPagePosts(); - * $I->assertQueries('Queries should be made to set the cache.') - * ``` - * - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueries() - */ - public function assertQueries($message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $posts = $this->factory()->post->create_many(3); - * wp_cache_set('page-posts', $posts, 'acme'); - * $pagePosts = $plugin->getPagePosts(); - * $I->assertNotQueries('Queries should not be made if the cache is set.') - * ``` - * - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueries() - */ - public function assertNotQueries($message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries have been made. - * - * @example - * ```php - * $posts = $this->factory()->post->create_many(3); - * $cachedUsers = $this->factory()->user->create_many(2); - * $nonCachedUsers = $this->factory()->user->create_many(2); - * foreach($cachedUsers as $userId){ - * wp_cache_set('page-posts-for-user-' . $userId, $posts, 'acme'); - * } - * // Run the same query as different users - * foreach(array_merge($cachedUsers, $nonCachedUsers) as $userId){ - * $pagePosts = $plugin->getPagePostsForUser($userId); - * } - * $I->assertCountQueries(2, 'A query should be made for each user missing cached posts.') - * ``` - * - * @param int $n The expected number of queries. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertCountQueries() - */ - public function assertCountQueries($n, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertCountQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least a query starting with the specified statement was made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_cache_flush(); - * cached_get_posts($args); - * $I->assertQueriesByStatement('SELECT'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatement() - */ - public function assertQueriesByStatement($statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query has been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $options = new Acme\Options(); - * $options->update('showAds', false); - * $I->assertQueriesByMethod('Acme\Options', 'update'); - * ``` - * - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByMethod() - */ - public function assertQueriesByMethod($class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->set('title', 'Peter Pan', $deferred = true); - * $this->assertNotQueriesByStatement('INSERT', 'Deferred write should happen on __destruct'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatement() - */ - public function assertNotQueriesByStatement($statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries starting with the specified statement were made. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->set('title', 'Peter Pan', $deferred = true); - * $repository->where('ID', 89)->set('title', 'Moby-dick', $deferred = true); - * $repository->where('ID', 2389)->set('title', 'The call of the wild', $deferred = false); - * $this->assertQueriesCountByStatement(1, 'INSERT', 'Deferred write should happen on __destruct'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatement() - */ - public function assertQueriesCountByStatement($n, $statement, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $options = new Acme\Options(); - * $options->update('adsSource', 'not-a-real-url.org'); - * $I->assertNotQueriesByMethod('Acme\Options', 'update'); - * ``` - * - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByMethod() - */ - public function assertNotQueriesByMethod($class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries have been made by the specified class method. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $bookRepository = new Acme\BookRepository(); - * $repository->where('ID', 23)->commit('title', 'Peter Pan'); - * $repository->where('ID', 89)->commit('title', 'Moby-dick'); - * $repository->where('ID', 2389)->commit('title', 'The call of the wild'); - * $this->assertQueriesCountByMethod(3, 'Acme\BookRepository', 'commit'); - * ``` - * @param int $n The expected number of queries. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByMethod() - */ - public function assertQueriesCountByMethod($n, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * acme_clean_queue(); - * $this->assertQueriesByFunction('acme_clean_queue'); - * ``` - * - * @param string $function The fully qualified name of the function to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByFunction() - */ - public function assertQueriesByFunction($function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $this->assertEmpty(Acme\get_orphaned_posts()); - * Acme\delete_orphaned_posts(); - * $this->assertNotQueriesByFunction('Acme\delete_orphaned_posts'); - * ``` - * - * @param string $function The fully qualified name of the function to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByFunction() - */ - public function assertNotQueriesByFunction($function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified function. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * $this->assertCount(3, Acme\get_orphaned_posts()); - * Acme\delete_orphaned_posts(); - * $this->assertQueriesCountByFunction(3, 'Acme\delete_orphaned_posts'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $function The function to check the queries for. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByFunction() - */ - public function assertQueriesCountByFunction($n, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * $this->assertQueriesByStatementAndMethod('UPDATE', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndMethod() - */ - public function assertQueriesByStatementAndMethod($statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * $this->assertQueriesByStatementAndMethod('INSERT', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndMethod() - */ - public function assertNotQueriesByStatementAndMethod($statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified class method starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * Acme\BookRepository::new(['title' => 'Alice in Wonderland'])->commit(); - * Acme\BookRepository::new(['title' => 'Moby-Dick'])->commit(); - * Acme\BookRepository::new(['title' => 'The Call of the Wild'])->commit(); - * $this->assertQueriesCountByStatementAndMethod(3, 'INSERT', Acme\BookRepository::class, 'commit'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $class The fully qualified name of the class to check. - * @param string $method The name of the method to check. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndMethod() - */ - public function assertQueriesCountByStatementAndMethod($n, $statement, $class, $method, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndMethod', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['post_type' => 'book', 'post_title' => 'Alice in Wonderland']); - * $this->assertQueriesByStatementAndFunction('INSERT', 'wp_insert_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The fully qualified function name. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndFunction() - */ - public function assertQueriesByStatementAndFunction($statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['ID' => $bookId, 'post_title' => 'The Call of the Wild']); - * $this->assertNotQueriesByStatementAndFunction('INSERT', 'wp_insert_post'); - * $this->assertQueriesByStatementAndFunction('UPDATE', 'wp_insert_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The name of the function to check the assertions for. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndFunction() - */ - public function assertNotQueriesByStatementAndFunction($statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made by the specified function starting with the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * wp_insert_post(['post_type' => 'book', 'post_title' => 'The Call of the Wild']); - * wp_insert_post(['post_type' => 'book', 'post_title' => 'Alice in Wonderland']); - * wp_insert_post(['post_type' => 'book', 'post_title' => 'The Chocolate Factory']); - * $this->assertQueriesCountByStatementAndFunction(3, 'INSERT', 'wp_insert_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $function The fully-qualified function name. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndFunction() - */ - public function assertQueriesCountByStatementAndFunction($n, $statement, $function, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndFunction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookId, 'post_title' => 'New Title']); - * $this->assertQueriesByAction('edit_post'); - * ``` - * - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByAction() - */ - public function assertQueriesByAction($action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookId); - * $this->assertNotQueriesByAction('edit_post'); - * ``` - * - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByAction() - */ - public function assertNotQueriesByAction($action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified action. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookOneId, 'post_title' => 'One']); - * wp_update_post(['ID' => $bookTwoId, 'post_title' => 'Two']); - * wp_update_post(['ID' => $bookThreeId, 'post_title' => 'Three']); - * $this->assertQueriesCountByAction(3, 'edit_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByAction() - */ - public function assertQueriesCountByAction($n, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified action containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_update_post(['ID' => $bookId, 'post_title' => 'New']); - * $this->assertQueriesByStatementAndAction('UPDATE', 'edit_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndAction() - */ - public function assertQueriesByStatementAndAction($statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified action containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookId); - * $this->assertNotQueriesByStatementAndAction('DELETE', 'delete_post'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndAction() - */ - public function assertNotQueriesByStatementAndAction($statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified action containing the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_action( 'edit_post', function($postId){ - * $count = get_option('acme_title_updates_count'); - * update_option('acme_title_updates_count', ++$count); - * } ); - * wp_delete_post($bookOneId); - * wp_delete_post($bookTwoId); - * wp_update_post(['ID' => $bookThreeId, 'post_title' => 'New']); - * $this->assertQueriesCountByStatementAndAction(2, 'DELETE', 'delete_post'); - * $this->assertQueriesCountByStatementAndAction(1, 'INSERT', 'edit_post'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $action The action name, e.g. 'init'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndAction() - */ - public function assertQueriesCountByStatementAndAction($n, $statement, $action, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookId)->post_title, $bookId); - * $this->assertQueriesByFilter('the_title'); - * ``` - * - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByFilter() - */ - public function assertQueriesByFilter($filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $this->assertNotQueriesByFilter('the_title'); - * ``` - * - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByFilter() - */ - public function assertNotQueriesByFilter($filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified filter. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookOneId)->post_title, $bookOneId); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $title = apply_filters('the_title', get_post($bookTwoId)->post_title, $bookTwoId); - * $this->assertQueriesCountByFilter(2, 'the_title'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByFilter() - */ - public function assertQueriesCountByFilter($n, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that at least one query was made as a consequence of the specified filter containing the SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($bookId)->post_title, $bookId); - * $this->assertQueriesByStatementAndFilter('SELECT', 'the_title'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesByStatementAndFilter() - */ - public function assertQueriesByStatementAndFilter($statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that no queries were made as a consequence of the specified filter containing the specified SQL query. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * $title = apply_filters('the_title', get_post($notABookId)->post_title, $notABookId); - * $this->assertNotQueriesByStatementAndFilter('SELECT', 'the_title'); - * ``` - * - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertNotQueriesByStatementAndFilter() - */ - public function assertNotQueriesByStatementAndFilter($statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotQueriesByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that n queries were made as a consequence of the specified filter containing the specified SQL statement. - * - * Queries generated by `setUp`, `tearDown` and `factory` methods are excluded by default. - * - * @example - * ```php - * add_filter('the_title', function($title, $postId){ - * $post = get_post($postId); - * if($post->post_type !== 'book'){ - * return $title; - * } - * $new = get_option('acme_new_prefix'); - * return "{$new} - " . $title; - * }); - * // Warm up the cache. - * $title = apply_filters('the_title', get_post($bookOneId)->post_title, $bookOneId); - * // Cache is warmed up now. - * $title = apply_filters('the_title', get_post($bookTwoId)->post_title, $bookTwoId); - * $title = apply_filters('the_title', get_post($bookThreeId)->post_title, $bookThreeId); - * $this->assertQueriesCountByStatementAndFilter(1, 'SELECT', 'the_title'); - * ``` - * - * @param int $n The expected number of queries. - * @param string $statement A simple string the statement should start with or a valid regular expression. - * Regular expressions must contain delimiters. - * @param string $filter The filter name, e.g. 'posts_where'. - * @param string $message An optional message to override the default one. - * - * @return void - * @see \Codeception\Module\WPQueries::assertQueriesCountByStatementAndFilter() - */ - public function assertQueriesCountByStatementAndFilter($n, $statement, $filter, $message = "") { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertQueriesCountByStatementAndFilter', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the current number of queries. - * Set-up and tear-down queries performed by the test case are filtered out. - * - * @example - * ```php - * // In a WPTestCase, using the global $wpdb object. - * $queriesCount = $this->queries()->countQueries(); - * // In a WPTestCase, using a custom $wpdb object. - * $queriesCount = $this->queries()->countQueries($customWdbb); - * ``` - * - * @param \wpdb|null $wpdb A specific instance of the `wpdb` class or `null` to use the global one. - * - * @return int The current count of performed queries. - * @see \Codeception\Module\WPQueries::countQueries() - */ - public function countQueries($wpdb = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('countQueries', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Returns the queries currently performed by the global database object or the specified one. - * Set-up and tear-down queries performed by the test case are filtered out. - * - * @example - * ```php - * // In a WPTestCase, using the global $wpdb object. - * $queries = $this->queries()->getQueries(); - * // In a WPTestCase, using a custom $wpdb object. - * $queries = $this->queries()->getQueries($customWdbb); - * ``` - * - * @param null|\wpdb $wpdb A specific instance of the `wpdb` class or `null` to use the global one. - * - * @return array An array of queries. - * @see \Codeception\Module\WPQueries::getQueries() - */ - public function getQueries($wpdb = NULL) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('getQueries', func_get_args())); - } -} diff --git a/tests/muwpunit/Replacement_Key_Test.php b/tests/muwpunit/Replacement_Key_Test.php index 23e972ec..e9d1dc6e 100644 --- a/tests/muwpunit/Replacement_Key_Test.php +++ b/tests/muwpunit/Replacement_Key_Test.php @@ -23,7 +23,7 @@ class Replacement_Key_Test extends UplinkTestCase { */ private $resource; - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->resource = Register::plugin( diff --git a/tests/wpunit.suite.dist.yml b/tests/wpunit.suite.dist.yml index 5c91b363..c4f4bb71 100644 --- a/tests/wpunit.suite.dist.yml +++ b/tests/wpunit.suite.dist.yml @@ -18,3 +18,14 @@ modules: adminEmail: admin@tribe.localhost title: 'StellarWP Uplink Tests' theme: twentytwenty + multisite: false + +env: + # nothing changes + singlesite: + # Force WPLoader to enable multisite + multisite: + modules: + config: + WPLoader: + multisite: true diff --git a/tests/wpunit/API/V3/AuthUrlTest.php b/tests/wpunit/API/V3/AuthUrlTest.php new file mode 100644 index 00000000..3d5a9e71 --- /dev/null +++ b/tests/wpunit/API/V3/AuthUrlTest.php @@ -0,0 +1,96 @@ +container->get( \StellarWP\Uplink\API\V3\Auth\Contracts\Auth_Url::class ); + + $this->assertInstanceOf( Auth_Url_Cache_Decorator::class, $auth_url ); + } + + public function test_it_gets_an_auth_url(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 200, + ], + 'body' => json_decode( '{"success":true,"data":{"status":200,"auth_url":"https://www.kadencewp.com/account-auth/"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $auth_url = $this->container->get( Auth_Url::class )->get( 'kadence-blocks-pro' ); + + $this->assertSame( 'https://www.kadencewp.com/account-auth/', $auth_url ); + } + + public function test_it_does_not_get_an_auth_url(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 404, + ], + 'body' => json_decode( '{"success":false,"data":{"status":404,"message":"Auth URL Not Found"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $auth_url = $this->container->get( Auth_Url::class )->get( 'kadence-blocks-pro' ); + + $this->assertSame( '', $auth_url ); + } + + public function test_it_caches_a_valid_auth_url(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 200, + ], + 'body' => json_decode( '{"success":true,"data":{"status":200,"auth_url":"https://www.kadencewp.com/account-auth/"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $auth_url = $this->container->get( Auth_Url_Cache_Decorator::class )->get( 'kadence-blocks-pro' ); + + $this->assertSame( 'https://www.kadencewp.com/account-auth/', $auth_url ); + $this->assertSame( 'https://www.kadencewp.com/account-auth/', get_transient( Auth_Url_Cache_Decorator::TRANSIENT_PREFIX . 'kadence_blocks_pro' ) ); + } + + public function test_it_caches_an_empty_auth_url(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 404, + ], + 'body' => json_decode( '{"success":false,"data":{"status":404,"message":"Auth URL Not Found"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $auth_url = $this->container->get( Auth_Url_Cache_Decorator::class )->get( 'kadence-blocks-pro' ); + + $this->assertSame( '', $auth_url ); + $this->assertSame( '', get_transient( Auth_Url_Cache_Decorator::TRANSIENT_PREFIX . 'kadence_blocks_pro' ) ); + } + +} diff --git a/tests/wpunit/API/V3/AuthorizerTest.php b/tests/wpunit/API/V3/AuthorizerTest.php new file mode 100644 index 00000000..a0053fd4 --- /dev/null +++ b/tests/wpunit/API/V3/AuthorizerTest.php @@ -0,0 +1,68 @@ +makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 200, + ], + 'body' => json_decode( '{"success":true,"data":{"status":200,"message":"Authorized"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $authorizer = $this->container->get( Token_Authorizer::class )->is_authorized( '1234', 'dc2c98d9-9ff8-4409-bfd2-a3cce5b5c840', 'test.com' ); + + $this->assertTrue( $authorizer ); + } + + public function test_it_does_not_authorize_an_invalid_license_key(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 404, + ], + 'body' => json_decode( '{"success":false,"data":{"status":404,"message":"License Key Not Found"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $authorizer = $this->container->get( Token_Authorizer::class )->is_authorized( 'invalid-license-key', 'dc2c98d9-9ff8-4409-bfd2-a3cce5b5c840', 'test.com' ); + + $this->assertFalse( $authorizer ); + } + + public function test_it_does_not_authorize_an_invalid_token(): void { + $clientMock = $this->makeEmpty( Client_V3::class, [ + 'get' => static function () { + return [ + 'response' => [ + 'code' => 401, + ], + 'body' => json_decode( '{"success":false,"data":{"status":401,"message":"Unauthorized"}}', true ), + ]; + }, + ] ); + + $this->container->bind( Client_V3::class, $clientMock ); + + $authorizer = $this->container->get( Token_Authorizer::class )->is_authorized( '1234', 'invalid', 'test.com' ); + + $this->assertFalse( $authorizer ); + } + +} diff --git a/tests/wpunit/API/Validation_ResponseTest.php b/tests/wpunit/API/Validation_ResponseTest.php index 0e3153cf..dc732476 100644 --- a/tests/wpunit/API/Validation_ResponseTest.php +++ b/tests/wpunit/API/Validation_ResponseTest.php @@ -11,7 +11,7 @@ class Validation_ResponseTest extends UplinkTestCase { public $resource; - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->resource = $this->getMockBuilder( Plugin::class )->setConstructorArgs( [ 'sample', diff --git a/tests/wpunit/Admin/NoticeTest.php b/tests/wpunit/Admin/NoticeTest.php index bbbbab38..44a8dedc 100644 --- a/tests/wpunit/Admin/NoticeTest.php +++ b/tests/wpunit/Admin/NoticeTest.php @@ -19,7 +19,9 @@ public function test_it_should_display_notice() { $notice = new Notice(); $notice->add_notice( Notice::EXPIRED_KEY, 'uplink' ); - $this->expectOutputString( $notice->setup_notices() ); + $this->expectOutputString( '' ); + + $notice->setup_notices(); } } diff --git a/tests/wpunit/Admin/Package_HandlerTest.php b/tests/wpunit/Admin/Package_HandlerTest.php index 3942fc4f..4af7cac5 100644 --- a/tests/wpunit/Admin/Package_HandlerTest.php +++ b/tests/wpunit/Admin/Package_HandlerTest.php @@ -3,17 +3,20 @@ namespace wpunit\Admin; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; use StellarWP\Uplink\Admin\Package_Handler; use StellarWP\Uplink\Tests\UplinkTestCase; class Package_HandlerTest extends UplinkTestCase { + use ProphecyTrait; + /** - * @var \WP_Filesystem_Base + * @var \WP_Filesystem_Base|\Prophecy\Prophecy\ObjectProphecy */ - protected $filesystem; + private $filesystem; - public function setUp() { + protected function setUp(): void { parent::setUp(); require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; diff --git a/tests/wpunit/Admin/Plugins_PageTest.php b/tests/wpunit/Admin/Plugins_PageTest.php index af92d1d9..0e4b8c4c 100644 --- a/tests/wpunit/Admin/Plugins_PageTest.php +++ b/tests/wpunit/Admin/Plugins_PageTest.php @@ -9,7 +9,7 @@ class Plugins_PageTest extends UplinkTestCase { - public function setUp() { + protected function setUp(): void { parent::setUp(); Register::plugin( diff --git a/tests/wpunit/Admin/Update_PreventionTest.php b/tests/wpunit/Admin/Update_PreventionTest.php index ee0d9af1..9d72ea21 100644 --- a/tests/wpunit/Admin/Update_PreventionTest.php +++ b/tests/wpunit/Admin/Update_PreventionTest.php @@ -11,7 +11,7 @@ class Update_PreventionTest extends UplinkTestCase { public $resource; public $path; - public function setUp() { + protected function setUp(): void { parent::setUp(); require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $this->path = 'uplink/plugin.php'; diff --git a/tests/wpunit/Auth/Admin/ConnectControllerTest.php b/tests/wpunit/Auth/Admin/ConnectControllerTest.php new file mode 100644 index 00000000..b8259a5b --- /dev/null +++ b/tests/wpunit/Auth/Admin/ConnectControllerTest.php @@ -0,0 +1,368 @@ +assertSame( + 'kadence_' . Token_Manager::TOKEN_SUFFIX, + $this->container->get( Config::TOKEN_OPTION_NAME ) + ); + + $this->token_manager = $this->container->get( Token_Manager::class ); + + // Register the sample plugin as a developer would in their plugin. + Register::plugin( + $this->slug, + 'Lib Sample', + '1.0.10', + 'uplink/index.php', + Sample_Plugin::class + ); + } + + protected function tearDown(): void { + $GLOBALS['current_screen'] = null; + + parent::tearDown(); + } + + public function test_it_stores_basic_token_data(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + } + + public function test_it_sets_additional_license_key(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key() ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + $license = '123456'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = $license; + $_GET[ Connect_Controller::SLUG ] = $this->slug; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + $this->assertSame( $plugin->get_license_key(), $license ); + } + + public function test_it_does_not_store_with_an_invalid_nonce(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $this->assertNull( $this->token_manager->get() ); + + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = 'wrong_nonce'; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertNull( $this->token_manager->get() ); + } + + public function test_it_does_not_store_an_invalid_token(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = 'invalid-token-format'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertNull( $this->token_manager->get() ); + } + + public function test_it_stores_token_but_not_license_without_a_slug(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key() ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + $license = '123456'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = $license; + $_GET[ Connect_Controller::SLUG ] = ''; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + $this->assertEmpty( $plugin->get_license_key() ); + } + + public function test_it_stores_token_but_not_license_with_a_slug_that_does_not_exist(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key() ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + $license = '123456'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = $license; + $_GET[ Connect_Controller::SLUG ] = 'a-plugin-slug-that-does-not-exist'; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + $this->assertEmpty( $plugin->get_license_key() ); + } + + public function test_it_stores_token_but_not_license_without_a_license(): void { + global $_GET; + + wp_set_current_user( 1 ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key() ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = ''; + $_GET[ Connect_Controller::SLUG ] = $this->slug; + + // Mock we're an admin inside the dashboard. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + $this->assertEmpty( $plugin->get_license_key() ); + } + + /** + * @env multisite + */ + public function test_it_sets_token_and_additional_license_key_on_multisite_network(): void { + global $_GET; + + // Create a subsite, but we won't use it. + $sub_site_id = wpmu_create_blog( 'wordpress.test', '/sub1', 'Test Subsite', 1 ); + $this->assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + $this->assertTrue( is_multisite() ); + wp_set_current_user( 1 ); + + // Mock our sample plugin is network activated, otherwise license key check fails. + $this->assertTrue( update_site_option( 'active_sitewide_plugins', [ + 'uplink/index.php' => time(), + ] ) ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key( 'network' ) ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + $license = '123456'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = $license; + $_GET[ Connect_Controller::SLUG ] = $this->slug; + + // Mock we're an admin inside the NETWORK dashboard. + $screen = WP_Screen::get( 'dashboard-network' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertTrue( $screen->in_admin( 'network' ) ); + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertSame( $token, $this->token_manager->get() ); + $this->assertSame( $plugin->get_license_key( 'network' ), $license ); + } + + /** + * @env multisite + */ + public function test_it_does_not_store_token_data_on_a_subsite(): void { + global $_GET; + + // Create a subsite. + $sub_site_id = wpmu_create_blog( 'wordpress.test', '/sub1', 'Test Subsite', 1 ); + $this->assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + $this->assertTrue( is_multisite() ); + + // Use this site, which should not allow a token to be set. + switch_to_blog( $sub_site_id ); + wp_set_current_user( 1 ); + + // Mock our sample plugin is network activated, otherwise license key check fails. + $this->assertTrue( update_site_option( 'active_sitewide_plugins', [ + 'uplink/index.php' => time(), + ] ) ); + + $plugin = $this->container->get( Collection::class )->offsetGet( $this->slug ); + $this->assertEmpty( $plugin->get_license_key( 'network' ) ); + + $this->assertNull( $this->token_manager->get() ); + + $nonce = ( $this->container->get( Nonce::class ) )->create(); + $token = '53ca40ab-c6c7-4482-a1eb-14c56da31015'; + $license = '123456'; + + // Mock these were passed via the query string. + $_GET[ Connect_Controller::TOKEN ] = $token; + $_GET[ Connect_Controller::NONCE ] = $nonce; + $_GET[ Connect_Controller::LICENSE ] = $license; + $_GET[ Connect_Controller::SLUG ] = $this->slug; + + // Mock we're in the subsite admin. + $screen = WP_Screen::get( 'dashboard' ); + $GLOBALS['current_screen'] = $screen; + + $this->assertFalse( $screen->in_admin( 'network' ) ); + $this->assertTrue( $screen->in_admin() ); + + // Fire off the action the Connect_Controller is running under. + do_action( 'admin_init' ); + + $this->assertNull( $this->token_manager->get() ); + $this->assertEmpty( $plugin->get_license_key( 'all' ) ); + } + +} diff --git a/tests/wpunit/Auth/AuthorizerTest.php b/tests/wpunit/Auth/AuthorizerTest.php new file mode 100644 index 00000000..2be66851 --- /dev/null +++ b/tests/wpunit/Auth/AuthorizerTest.php @@ -0,0 +1,115 @@ +authorizer = $this->container->get( Authorizer::class ); + } + + public function test_it_does_not_authorize_for_logged_out_users(): void { + $this->assertFalse( is_user_logged_in() ); + $this->assertFalse( $this->authorizer->can_auth() ); + } + + public function test_it_authorizes_a_single_site(): void { + wp_set_current_user( 1 ); + + $this->assertTrue( is_user_logged_in() ); + $this->assertTrue( is_super_admin() ); + $this->assertTrue( $this->authorizer->can_auth() ); + } + + /** + * @env multisite + */ + public function test_it_does_not_authorize_a_subsite_with_multisite_subfolders_enabled(): void { + $this->assertTrue( is_multisite() ); + + // Main test domain is wordpress.test, create a subfolder sub-site. + $sub_site_id = wpmu_create_blog( 'wordpress.test', '/sub1', 'Test Subsite', 1 ); + + $this->assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + wp_set_current_user( 1 ); + $this->assertTrue( is_user_logged_in() ); + $this->assertTrue( is_super_admin() ); + + $this->assertFalse( $this->authorizer->can_auth() ); + } + + /** + * @env multisite + */ + public function test_it_does_not_authorize_a_subsite_with_an_existing_network_token(): void { + $this->assertTrue( is_multisite() ); + + $token_manager = $this->container->get( Token_Manager::class ); + $token = '695be4b3-ad6e-4863-9287-3052f597b1f6'; + + // Store a token while on the main site. + $this->assertTrue( $token_manager->store( '695be4b3-ad6e-4863-9287-3052f597b1f6' ) ); + + $this->assertSame( $token, get_network_option( get_current_network_id(), $token_manager->option_name() ) ); + $this->assertEmpty( get_option( $token_manager->option_name() ) ); + + // Main test domain is wordpress.test, create a sub domain. + $sub_site_id = wpmu_create_blog( 'sub1.wordpress.test', '/', 'Test Subsite', 1 ); + + $this->assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + wp_set_current_user( 1 ); + $this->assertTrue( is_user_logged_in() ); + $this->assertTrue( is_super_admin() ); + + $this->assertFalse( $this->authorizer->can_auth() ); + } + + /** + * @env multisite + */ + public function test_it_authorizes_a_subsite(): void { + $this->assertTrue( is_multisite() ); + + // Main test domain is wordpress.test, create a completely custom domain. + $sub_site_id = wpmu_create_blog( 'custom.test', '/', 'Test Subsite', 1 ); + + $this->assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + wp_set_current_user( 1 ); + $this->assertTrue( is_user_logged_in() ); + $this->assertTrue( is_super_admin() ); + + $this->assertTrue( $this->authorizer->can_auth() ); + } + +} diff --git a/tests/wpunit/Auth/NonceTest.php b/tests/wpunit/Auth/NonceTest.php new file mode 100644 index 00000000..5e8305dd --- /dev/null +++ b/tests/wpunit/Auth/NonceTest.php @@ -0,0 +1,77 @@ +nonce = $this->container->get( Nonce::class ); + } + + public function test_it_creates_a_nonce(): void { + $nonce = $this->nonce->create(); + + $this->assertNotEmpty( $nonce ); + $this->assertSame( 16, strlen( $nonce ) ); + $this->assertFalse( Nonce::verify( '') ); + $this->assertTrue( Nonce::verify( $nonce ) ); + } + + public function test_it_creates_a_nonce_url(): void { + $url = 'http://wordpress.test/wp-admin/import.php'; + $nonce_url = $this->nonce->create_url( $url ); + + $this->assertStringStartsWith( + 'http://wordpress.test/wp-admin/import.php?_uplink_nonce=', + $nonce_url + ); + + $query = wp_parse_url( $nonce_url, PHP_URL_QUERY ); + + parse_str( $query, $parts ); + + $nonce = $parts[ '_uplink_nonce' ]; + + $this->assertNotEmpty( $nonce ); + $this->assertSame( 16, strlen( $nonce ) ); + $this->assertFalse( Nonce::verify( '') ); + $this->assertTrue( Nonce::verify( $nonce ) ); + } + + public function test_it_creates_a_nonce_url_with_extra_query_arguments(): void { + $url = 'http://wordpress.test/wp-admin/post.php?post=1&action=edit'; + + // URL is escaped, reverse that. + $nonce_url = html_entity_decode( $this->nonce->create_url( $url ) ); + + $this->assertStringStartsWith( + 'http://wordpress.test/wp-admin/post.php?post=1&action=edit&_uplink_nonce=', + $nonce_url + ); + + $query = wp_parse_url( $nonce_url, PHP_URL_QUERY ); + + parse_str( $query, $parts ); + + $nonce = $parts['_uplink_nonce']; + + $this->assertNotEmpty( $nonce ); + $this->assertSame( 16, strlen( $nonce ) ); + $this->assertFalse( Nonce::verify( '' ) ); + $this->assertTrue( Nonce::verify( $nonce ) ); + } + +} diff --git a/tests/wpunit/Auth/Token/CustomDomainMultisiteTokenMangerTest.php b/tests/wpunit/Auth/Token/CustomDomainMultisiteTokenMangerTest.php new file mode 100644 index 00000000..8403827c --- /dev/null +++ b/tests/wpunit/Auth/Token/CustomDomainMultisiteTokenMangerTest.php @@ -0,0 +1,117 @@ +assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + $this->token_manager = $this->container->get( Token_Manager::class ); + } + + /** + * @env multisite + */ + public function test_it_stores_and_retrieves_a_token_from_the_network(): void { + $this->assertTrue( is_multisite() ); + + $this->assertNull( $this->token_manager->get() ); + + $token = 'cd4b77be-985f-4737-89b7-eaa13b335fe8'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->assertSame( $token, get_network_option( get_current_network_id(), $this->token_manager->option_name() ) ); + $this->assertEmpty( get_option( $this->token_manager->option_name() ) ); + } + + /** + * @env multisite + */ + public function test_it_deletes_a_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $token = 'b5aad022-71ca-4b29-85c2-70da5c8a5779'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->token_manager->delete(); + + $this->assertNull( $this->token_manager->get() ); + $this->assertEmpty( get_network_option( get_current_network_id(), $this->token_manager->option_name() ) ); + } + + /** + * @env multisite + */ + public function test_it_does_not_store_an_empty_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $this->assertFalse( $this->token_manager->store( '' ) ); + + $this->assertNull( $this->token_manager->get() ); + } + + /** + * @env multisite + */ + public function test_it_validates_proper_tokens(): void { + $tokens = [ + '93280f34-9054-42fb-8f90-e22830eb3225', + '565435c0-f601-4ba0-ae4e-982b83460f34', + 'a6e8d999-8b55-47ed-8798-0adb608083a6', + '21897516-419a-4a4c-b0d5-9e21e6506421', + '09f7f9ea-f931-4600-9739-97fa6ac0d454' + ]; + + foreach ( $tokens as $token ) { + $this->assertTrue( $this->token_manager->validate( $token ) ); + } + } + + /** + * @env multisite + */ + public function test_it_does_validate_invalid_tokens(): void { + $tokens = [ + '4c79d900-5656-11ee-8c99-0242ac120002', + '6d12a782-5656-11ee-8c99-0242ac120002', + 'invalid', + '', + ]; + + foreach ( $tokens as $token ) { + $this->assertFalse( $this->token_manager->validate( $token ) ); + } + } + +} diff --git a/tests/wpunit/Auth/Token/SingleSiteTokenMangerTest.php b/tests/wpunit/Auth/Token/SingleSiteTokenMangerTest.php new file mode 100644 index 00000000..23eece6b --- /dev/null +++ b/tests/wpunit/Auth/Token/SingleSiteTokenMangerTest.php @@ -0,0 +1,74 @@ +token_manager = $this->container->get( Token_Manager::class ); + } + + /** + * @env singlesite + */ + public function test_it_stores_and_retrieves_a_token(): void { + $this->assertFalse( is_multisite() ); + + $this->assertNull( $this->token_manager->get() ); + + $token = 'b0679a2e-b36d-41ca-8121-f43267751938'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->assertSame( $token, get_option( $this->token_manager->option_name() ) ); + + } + + /** + * @env singlesite + */ + public function test_it_deletes_a_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $token = 'b5aad022-71ca-4b29-85c2-70da5c8a5779'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->token_manager->delete(); + + $this->assertNull( $this->token_manager->get() ); + } + + /** + * @env singlesite + */ + public function test_it_does_not_store_an_empty_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $this->assertFalse( $this->token_manager->store( '' ) ); + + $this->assertNull( $this->token_manager->get() ); + } + +} diff --git a/tests/wpunit/Auth/Token/SubDomainMultisiteTokenMangerTest.php b/tests/wpunit/Auth/Token/SubDomainMultisiteTokenMangerTest.php new file mode 100644 index 00000000..c61d01bb --- /dev/null +++ b/tests/wpunit/Auth/Token/SubDomainMultisiteTokenMangerTest.php @@ -0,0 +1,83 @@ +assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + $this->token_manager = $this->container->get( Token_Manager::class ); + } + + /** + * @env multisite + */ + public function test_it_stores_and_retrieves_a_token_from_the_network(): void { + $this->assertTrue( is_multisite() ); + + $this->assertNull( $this->token_manager->get() ); + + $token = 'cd4b77be-985f-4737-89b7-eaa13b335fe8'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->assertSame( $token, get_network_option( get_current_network_id(), $this->token_manager->option_name() ) ); + $this->assertEmpty( get_option( $this->token_manager->option_name() ) ); + } + + /** + * @env multisite + */ + public function test_it_deletes_a_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $token = 'b5aad022-71ca-4b29-85c2-70da5c8a5779'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->token_manager->delete(); + + $this->assertNull( $this->token_manager->get() ); + } + + /** + * @env multisite + */ + public function test_it_does_not_store_an_empty_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $this->assertFalse( $this->token_manager->store( '' ) ); + + $this->assertNull( $this->token_manager->get() ); + } + +} diff --git a/tests/wpunit/Auth/Token/SubfolderMultisiteTokenMangerTest.php b/tests/wpunit/Auth/Token/SubfolderMultisiteTokenMangerTest.php new file mode 100644 index 00000000..8b5613db --- /dev/null +++ b/tests/wpunit/Auth/Token/SubfolderMultisiteTokenMangerTest.php @@ -0,0 +1,83 @@ +assertNotInstanceOf( WP_Error::class, $sub_site_id ); + $this->assertGreaterThan( 1, $sub_site_id ); + + switch_to_blog( $sub_site_id ); + + $this->token_manager = $this->container->get( Token_Manager::class ); + } + + /** + * @env multisite + */ + public function test_it_stores_and_retrieves_a_token_from_the_network(): void { + $this->assertTrue( is_multisite() ); + + $this->assertNull( $this->token_manager->get() ); + + $token = 'ddbbec78-4439-4180-a6e8-1a63a1df4e2c'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->assertSame( $token, get_network_option( get_current_network_id(), $this->token_manager->option_name() ) ); + $this->assertEmpty( get_option( $this->token_manager->option_name() ) ); + } + + /** + * @env multisite + */ + public function test_it_deletes_a_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $token = 'b5aad022-71ca-4b29-85c2-70da5c8a5779'; + + $this->assertTrue( $this->token_manager->store( $token ) ); + + $this->assertSame( $token, $this->token_manager->get() ); + + $this->token_manager->delete(); + + $this->assertNull( $this->token_manager->get() ); + } + + /** + * @env multisite + */ + public function test_it_does_not_store_an_empty_token(): void { + $this->assertNull( $this->token_manager->get() ); + + $this->assertFalse( $this->token_manager->store( '' ) ); + + $this->assertNull( $this->token_manager->get() ); + } + +} diff --git a/tests/wpunit/ConfigTest.php b/tests/wpunit/ConfigTest.php new file mode 100644 index 00000000..99ddcc02 --- /dev/null +++ b/tests/wpunit/ConfigTest.php @@ -0,0 +1,57 @@ +assertSame( + 'my_custom_prefix_' . Token_Manager::TOKEN_SUFFIX, + $this->container->get( Config::TOKEN_OPTION_NAME ) + ); + } + + public function test_it_sanitizes_invalid_prefixes(): void { + Config::set_token_auth_prefix( 'my~ invalid—prefix`' ); + + $this->assertSame( + 'my_invalid_prefix_' . Token_Manager::TOKEN_SUFFIX, + $this->container->get( Config::TOKEN_OPTION_NAME ) + ); + } + + public function test_it_sets_token_with_exactly_173_characters_and_no_trailing_hyphen(): void { + $prefix = 'fluffy_unicorn_rainbow_sunshine_happy_smile_peace_joy_love_puppy_harmony_giggles_dreams_celebrate_fantastic_wonderful_whimsical_serendipity_butterfly_magic_sparkle_sweetness'; + + Config::set_token_auth_prefix( $prefix ); + + $this->assertSame( + $prefix . '_' . Token_Manager::TOKEN_SUFFIX, + $this->container->get( Config::TOKEN_OPTION_NAME ) + ); + } + + public function test_it_sets_token_with_exactly_174_characters_and_a_trailing_hyphen(): void { + $prefix = 'fluffy_unicorn_rainbow_sunshine_happy_smile_peace_joy_love_puppy_harmony_giggles_dreams_celebrate_fantastic_wonderful_whimsical_serendipity_butterfly_magic_sparkle_sweetness_'; + + Config::set_token_auth_prefix( $prefix ); + + $this->assertSame( + $prefix . Token_Manager::TOKEN_SUFFIX, + $this->container->get( Config::TOKEN_OPTION_NAME ) + ); + } + + public function test_it_throws_exception_with_long_prefix(): void { + $this->expectException( InvalidArgumentException::class ); + $this->expectExceptionMessage( 'The token auth prefix must be at most 174 characters, including a trailing hyphen.' ); + Config::set_token_auth_prefix( 'fluffy_unicorn_rainbow_sunshine_happy_smile_peace_joy_love_puppy_harmony_giggles_dreams_celebrate_fantastic_wonderful_whimsical_serendipity_butterfly_magic_sparkle_sweetness_trust_' ); + } + +} diff --git a/tests/wpunit/Replacement_Key_Test.php b/tests/wpunit/Replacement_Key_Test.php index 5df20fc0..739f8a22 100644 --- a/tests/wpunit/Replacement_Key_Test.php +++ b/tests/wpunit/Replacement_Key_Test.php @@ -20,7 +20,7 @@ class Replacement_Key_Test extends UplinkTestCase { */ private $resource; - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->resource = Register::plugin( diff --git a/tests/wpunit/Resources/CollectionTest.php b/tests/wpunit/Resources/CollectionTest.php index 112be366..58f7b2ec 100644 --- a/tests/wpunit/Resources/CollectionTest.php +++ b/tests/wpunit/Resources/CollectionTest.php @@ -3,15 +3,16 @@ namespace StellarWP\Uplink\Tests\Resources; use StellarWP\Uplink\Config; +use StellarWP\Uplink\Tests\UplinkTestCase; use StellarWP\Uplink\Uplink; use StellarWP\Uplink\Register; use StellarWP\Uplink\Resources as Uplink_Resources; -class CollectionTest extends \StellarWP\Uplink\Tests\UplinkTestCase { +class CollectionTest extends UplinkTestCase { public $collection; public $container; - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->container = Config::get_container(); diff --git a/tests/wpunit/Site/DataTest.php b/tests/wpunit/Site/DataTest.php index 92284347..bbaa6f02 100644 --- a/tests/wpunit/Site/DataTest.php +++ b/tests/wpunit/Site/DataTest.php @@ -3,11 +3,12 @@ namespace StellarWP\Uplink\Tests\Site; use StellarWP\Uplink; +use StellarWP\Uplink\Tests\UplinkTestCase; -class DataTest extends \StellarWP\Uplink\Tests\UplinkTestCase { +class DataTest extends UplinkTestCase { public $container; - public function setUp() { + protected function setUp(): void { parent::setUp(); $this->container = Uplink\Config::get_container(); }