mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Update lib packages
This commit is contained in:
parent
238ebc0949
commit
b483115883
38 changed files with 1323 additions and 598 deletions
|
@ -26,7 +26,8 @@
|
||||||
"php-stubs/wordpress-stubs": "^5.0@stable",
|
"php-stubs/wordpress-stubs": "^5.0@stable",
|
||||||
"php-stubs/woocommerce-stubs": "^8.0@stable",
|
"php-stubs/woocommerce-stubs": "^8.0@stable",
|
||||||
"vimeo/psalm": "^4.0",
|
"vimeo/psalm": "^4.0",
|
||||||
"vlucas/phpdotenv": "^5"
|
"vlucas/phpdotenv": "^5",
|
||||||
|
"coenjacobs/mozart": "^0.7.1"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
310
composer.lock
generated
310
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2fa610ed883c0868838d3008b7127cbf",
|
"content-hash": "6ca9c2c7864d2649617db6d3850382c5",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "container-interop/service-provider",
|
"name": "container-interop/service-provider",
|
||||||
|
@ -904,6 +904,64 @@
|
||||||
},
|
},
|
||||||
"time": "2021-11-11T15:53:55+00:00"
|
"time": "2021-11-11T15:53:55+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "coenjacobs/mozart",
|
||||||
|
"version": "0.7.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/coenjacobs/mozart.git",
|
||||||
|
"reference": "dbcdeb992d20d9c8914eef090f9a0d684bb1102c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/coenjacobs/mozart/zipball/dbcdeb992d20d9c8914eef090f9a0d684bb1102c",
|
||||||
|
"reference": "dbcdeb992d20d9c8914eef090f9a0d684bb1102c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"league/flysystem": "^1.0",
|
||||||
|
"php": "^7.3|^8.0",
|
||||||
|
"symfony/console": "^4|^5",
|
||||||
|
"symfony/finder": "^4|^5"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mheap/phpunit-github-actions-printer": "^1.4",
|
||||||
|
"phpunit/phpunit": "^8.5",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5",
|
||||||
|
"vimeo/psalm": "^4.4"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/mozart"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"CoenJacobs\\Mozart\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Coen Jacobs",
|
||||||
|
"email": "coenjacobs@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Composes all dependencies as a package inside a WordPress plugin",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/coenjacobs/mozart/issues",
|
||||||
|
"source": "https://github.com/coenjacobs/mozart/tree/0.7.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/coenjacobs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2021-02-02T21:37:03+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/package-versions-deprecated",
|
"name": "composer/package-versions-deprecated",
|
||||||
"version": "1.11.99.5",
|
"version": "1.11.99.5",
|
||||||
|
@ -1828,31 +1886,34 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inpsyde/modularity",
|
"name": "inpsyde/modularity",
|
||||||
"version": "1.10.0",
|
"version": "1.12.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/inpsyde/modularity.git",
|
"url": "https://github.com/inpsyde/modularity.git",
|
||||||
"reference": "2119d0e32706741a3c6dc0a85d908ec19ebf142e"
|
"reference": "e1ca1c81b7b663355906b586525d21ac5d46bc65"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/inpsyde/modularity/zipball/2119d0e32706741a3c6dc0a85d908ec19ebf142e",
|
"url": "https://api.github.com/repos/inpsyde/modularity/zipball/e1ca1c81b7b663355906b586525d21ac5d46bc65",
|
||||||
"reference": "2119d0e32706741a3c6dc0a85d908ec19ebf142e",
|
"reference": "e1ca1c81b7b663355906b586525d21ac5d46bc65",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": ">=7.4 <8.4",
|
"php": ">=7.4",
|
||||||
"psr/container": "^1.1.0 || ^2"
|
"psr/container": "^1.1.0 || ^2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"brain/monkey": "^2.6.1",
|
"brain/monkey": "^2.6.1",
|
||||||
"inpsyde/php-coding-standards": "^2@dev",
|
"inpsyde/wp-stubs-versions": "6.7",
|
||||||
"inpsyde/wp-stubs-versions": "dev-latest",
|
|
||||||
"mikey179/vfsstream": "^v1.6.11",
|
"mikey179/vfsstream": "^v1.6.11",
|
||||||
|
"phpstan/phpstan": "^2.1.1",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||||
|
"phpstan/phpstan-mockery": "^2.0.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^2.0.4",
|
||||||
"phpunit/phpunit": "^9.6.19",
|
"phpunit/phpunit": "^9.6.19",
|
||||||
"roots/wordpress-no-content": "@dev",
|
"swissspidy/phpstan-no-private": "^v1.0.0",
|
||||||
"vimeo/psalm": "^5.24.0"
|
"syde/phpcs": "^1.0.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@ -1871,18 +1932,168 @@
|
||||||
],
|
],
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Inpsyde GmbH",
|
"name": "Syde GmbH",
|
||||||
"email": "hello@inpsyde.com",
|
"email": "hello@syde.com",
|
||||||
"homepage": "https://inpsyde.com/",
|
"homepage": "https://syde.com/",
|
||||||
"role": "Company"
|
"role": "Company"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Modular PSR-11 implementation for WordPress plugins, themes or libraries.",
|
"description": "Modular PSR-11 implementation for WordPress plugins, themes or libraries.",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/inpsyde/modularity/issues",
|
"issues": "https://github.com/inpsyde/modularity/issues",
|
||||||
"source": "https://github.com/inpsyde/modularity/tree/1.10.0"
|
"source": "https://github.com/inpsyde/modularity/tree/1.12.0"
|
||||||
},
|
},
|
||||||
"time": "2024-09-03T10:42:50+00:00"
|
"time": "2025-05-09T12:13:17+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "league/flysystem",
|
||||||
|
"version": "1.1.10",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thephpleague/flysystem.git",
|
||||||
|
"reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1",
|
||||||
|
"reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"league/mime-type-detection": "^1.3",
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"league/flysystem-sftp": "<1.0.6"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpspec/prophecy": "^1.11.1",
|
||||||
|
"phpunit/phpunit": "^8.5.8"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ftp": "Allows you to use FTP server storage",
|
||||||
|
"ext-openssl": "Allows you to use FTPS server storage",
|
||||||
|
"league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
|
||||||
|
"league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
|
||||||
|
"league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
|
||||||
|
"league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
|
||||||
|
"league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
|
||||||
|
"league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
|
||||||
|
"league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
|
||||||
|
"league/flysystem-webdav": "Allows you to use WebDAV storage",
|
||||||
|
"league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
|
||||||
|
"spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
|
||||||
|
"srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.1-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\Flysystem\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Frank de Jonge",
|
||||||
|
"email": "info@frenky.net"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Filesystem abstraction: Many filesystems, one API.",
|
||||||
|
"keywords": [
|
||||||
|
"Cloud Files",
|
||||||
|
"WebDAV",
|
||||||
|
"abstraction",
|
||||||
|
"aws",
|
||||||
|
"cloud",
|
||||||
|
"copy.com",
|
||||||
|
"dropbox",
|
||||||
|
"file systems",
|
||||||
|
"files",
|
||||||
|
"filesystem",
|
||||||
|
"filesystems",
|
||||||
|
"ftp",
|
||||||
|
"rackspace",
|
||||||
|
"remote",
|
||||||
|
"s3",
|
||||||
|
"sftp",
|
||||||
|
"storage"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/thephpleague/flysystem/issues",
|
||||||
|
"source": "https://github.com/thephpleague/flysystem/tree/1.1.10"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://offset.earth/frankdejonge",
|
||||||
|
"type": "other"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-10-04T09:16:37+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "league/mime-type-detection",
|
||||||
|
"version": "1.16.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thephpleague/mime-type-detection.git",
|
||||||
|
"reference": "2d6702ff215bf922936ccc1ad31007edc76451b9"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9",
|
||||||
|
"reference": "2d6702ff215bf922936ccc1ad31007edc76451b9",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.2",
|
||||||
|
"phpstan/phpstan": "^0.12.68",
|
||||||
|
"phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\MimeTypeDetection\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Frank de Jonge",
|
||||||
|
"email": "info@frankdejonge.nl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Mime-type detection for Flysystem",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/thephpleague/mime-type-detection/issues",
|
||||||
|
"source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/frankdejonge",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/league/flysystem",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-21T08:32:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mockery/mockery",
|
"name": "mockery/mockery",
|
||||||
|
@ -4528,6 +4739,69 @@
|
||||||
],
|
],
|
||||||
"time": "2023-01-24T14:02:46+00:00"
|
"time": "2023-01-24T14:02:46+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/finder",
|
||||||
|
"version": "v5.4.45",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/finder.git",
|
||||||
|
"reference": "63741784cd7b9967975eec610b256eed3ede022b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b",
|
||||||
|
"reference": "63741784cd7b9967975eec610b256eed3ede022b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2.5",
|
||||||
|
"symfony/deprecation-contracts": "^2.1|^3",
|
||||||
|
"symfony/polyfill-php80": "^1.16"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Finder\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Finds files and directories via an intuitive fluent interface",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/finder/tree/v5.4.45"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-28T13:32:08+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.30.0",
|
"version": "v1.30.0",
|
||||||
|
@ -5541,8 +5815,8 @@
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"stability-flags": {
|
"stability-flags": {
|
||||||
"php-stubs/woocommerce-stubs": 0,
|
"php-stubs/wordpress-stubs": 0,
|
||||||
"php-stubs/wordpress-stubs": 0
|
"php-stubs/woocommerce-stubs": 0
|
||||||
},
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
|
@ -5550,7 +5824,7 @@
|
||||||
"php": "^7.4 | ^8.0",
|
"php": "^7.4 | ^8.0",
|
||||||
"ext-json": "*"
|
"ext-json": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": [],
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "7.4"
|
"php": "7.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -64,6 +64,8 @@ class AliasingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return $this->inner->has($this->getInnerKey($key));
|
return $this->inner->has($this->getInnerKey($key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ class CachingContainer implements ContainerInterface
|
||||||
throw new NotFoundException($this->__('Key "%1$s" not found in inner container', [$key]), 0, $e);
|
throw new NotFoundException($this->__('Key "%1$s" not found in inner container', [$key]), 0, $e);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new ContainerException(
|
throw new ContainerException(
|
||||||
$this->__('Could not retrieve value for key "%1$s from inner container', [$key]),
|
$this->__('Could not retrieve value for key "%1$s" from inner container', [$key]),
|
||||||
0,
|
0,
|
||||||
$e
|
$e
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,7 +38,8 @@ class CompositeCachingServiceProvider implements ServiceProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* @inheritDoc
|
||||||
|
*
|
||||||
* @psalm-suppress InvalidNullableReturnType
|
* @psalm-suppress InvalidNullableReturnType
|
||||||
* It isn't actually going to return null ever, because $factories will be filled during indexing.
|
* It isn't actually going to return null ever, because $factories will be filled during indexing.
|
||||||
*/
|
*/
|
||||||
|
@ -56,7 +57,8 @@ class CompositeCachingServiceProvider implements ServiceProviderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* @inheritDoc
|
||||||
|
*
|
||||||
* @psalm-suppress InvalidNullableReturnType
|
* @psalm-suppress InvalidNullableReturnType
|
||||||
* It isn't actually going to return null ever, because $factories will be filled during indexing.
|
* It isn't actually going to return null ever, because $factories will be filled during indexing.
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +80,7 @@ class CompositeCachingServiceProvider implements ServiceProviderInterface
|
||||||
*
|
*
|
||||||
* Caches them internally.
|
* Caches them internally.
|
||||||
*
|
*
|
||||||
* @param iterable|ServiceProviderInterface[] $providers The providers to index.
|
* @param iterable<ServiceProviderInterface> $providers The providers to index.
|
||||||
*/
|
*/
|
||||||
protected function indexProviderDefinitions(iterable $providers): void
|
protected function indexProviderDefinitions(iterable $providers): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -89,6 +89,7 @@ class DelegatingContainer implements ContainerInterface
|
||||||
public function has($id)
|
public function has($id)
|
||||||
{
|
{
|
||||||
$services = $this->provider->getFactories();
|
$services = $this->provider->getFactories();
|
||||||
|
$id = (string) $id;
|
||||||
|
|
||||||
return array_key_exists($id, $services);
|
return array_key_exists($id, $services);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,10 @@ class DeprefixingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
return $this->inner->has($this->getInnerKey($key)) || (!$this->strict && $this->inner->has($key));
|
$key = (string) $key;
|
||||||
|
$realKey = $this->getInnerKey($key);
|
||||||
|
|
||||||
|
return $this->inner->has($realKey) || (!$this->strict && $this->inner->has($key));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,13 +5,13 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
||||||
|
|
||||||
use ArrayIterator;
|
use ArrayIterator;
|
||||||
use Traversable;
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\WritableContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\WritableContainerInterface;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\WritableMapInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\WritableMapInterface;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Util\StringTranslatingTrait;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Util\StringTranslatingTrait;
|
||||||
use IteratorAggregate;
|
use IteratorAggregate;
|
||||||
use RangeException;
|
use RangeException;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple mutable dictionary, i.e. an enumerable key-value map.
|
* A simple mutable dictionary, i.e. an enumerable key-value map.
|
||||||
|
@ -54,6 +54,8 @@ class Dictionary implements
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
$isHas = array_key_exists($key, $this->data);
|
$isHas = array_key_exists($key, $this->data);
|
||||||
|
|
||||||
return $isHas;
|
return $isHas;
|
||||||
|
|
|
@ -61,6 +61,8 @@ class FlashContainer implements
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return array_key_exists($key, $this->flashData);
|
return array_key_exists($key, $this->flashData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,8 @@ class HierarchyContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return array_key_exists($key, $this->data);
|
return array_key_exists($key, $this->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,8 @@ class MappingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return $this->inner->has($key);
|
return $this->inner->has($key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,8 @@ class MaskingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return $this->isExposed($key) && $this->inner->has($key);
|
return $this->isExposed($key) && $this->inner->has($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\WritableMapInterface;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\ContainerException;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\ContainerException;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
||||||
use IteratorAggregate;
|
use IteratorAggregate;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container that does nothing.
|
* A container that does nothing.
|
||||||
|
@ -92,7 +93,7 @@ class NoOpContainer implements
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getIterator()
|
public function getIterator(): Traversable
|
||||||
{
|
{
|
||||||
return new ArrayIterator([]);
|
return new ArrayIterator([]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,8 @@ class PathContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @psalm-suppress InvalidCatch
|
* @psalm-suppress InvalidCatch
|
||||||
* The base interface does not extend Throwable, but in fact everything that is possible
|
* The base interface does not extend Throwable, but in fact everything that is possible
|
||||||
|
|
|
@ -5,9 +5,12 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Collection\ContainerInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\ContainerException;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\Exception\NotFoundException;
|
||||||
|
use Exception;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface as PsrContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface as PsrContainerInterface;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\NotFoundExceptionInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container implementation that wraps around an inner container and prefixes its keys, requiring consumers to
|
* A container implementation that wraps around an inner container and prefixes its keys, requiring consumers to
|
||||||
|
@ -89,11 +92,18 @@ class PrefixingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
if (!$this->isPrefixed($key) && $this->strict) {
|
if (!$this->isPrefixed($key) && $this->strict) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->inner->has($this->unprefix($key)) || (!$this->strict && $this->inner->has($key));
|
try {
|
||||||
|
$realKey = $this->unprefix($key);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new ContainerException(sprintf('Could not unprefix key "%1$s"', $key), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->inner->has($realKey) || (!$this->strict && $this->inner->has($key));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,7 +118,7 @@ class PrefixingContainer implements ContainerInterface
|
||||||
protected function unprefix(string $key): string
|
protected function unprefix(string $key): string
|
||||||
{
|
{
|
||||||
return $this->isPrefixed($key)
|
return $this->isPrefixed($key)
|
||||||
? substr($key, strlen($this->prefix))
|
? $this->substring($key, strlen($this->prefix))
|
||||||
: $key;
|
: $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,4 +135,35 @@ class PrefixingContainer implements ContainerInterface
|
||||||
{
|
{
|
||||||
return strlen($this->prefix) > 0 && strpos($key, $this->prefix) === 0;
|
return strlen($this->prefix) > 0 && strpos($key, $this->prefix) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a substring from the specified string.
|
||||||
|
*
|
||||||
|
* @see substr()
|
||||||
|
*
|
||||||
|
* @param string $string The string to extract from.
|
||||||
|
* @param int $offset The char position, at which to start extraction.
|
||||||
|
* @param int|null $length The char position, at which to end extraction; unlimited if `null`.
|
||||||
|
*
|
||||||
|
* @return string The extracted substring.
|
||||||
|
*
|
||||||
|
* @throws RuntimeException If unable to extract.
|
||||||
|
*/
|
||||||
|
protected function substring(string $string, int $offset = 0, ?int $length = null): string
|
||||||
|
{
|
||||||
|
$length = $length ?? strlen($string) - $offset;
|
||||||
|
$substring = substr($string, $offset, $length);
|
||||||
|
if ($substring === false) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
sprintf(
|
||||||
|
'Could not extract substring starting at %1$d of length %2$s from string "%3$s"',
|
||||||
|
$offset,
|
||||||
|
$length ?: 'null',
|
||||||
|
$string
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $substring;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,8 @@ class SegmentingContainer implements ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function has($key)
|
public function has($key)
|
||||||
{
|
{
|
||||||
|
$key = (string) $key;
|
||||||
|
|
||||||
return $this->inner->has($key);
|
return $this->inner->has($key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,22 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value object capable of providing services.
|
* A value object capable of providing services.
|
||||||
*
|
*
|
||||||
* @package Dhii\Di
|
* @psalm-type Factory = callable(ContainerInterface): mixed
|
||||||
|
* @psalm-type Extension = callable(ContainerInterface, mixed): mixed
|
||||||
*/
|
*/
|
||||||
class ServiceProvider implements ServiceProviderInterface
|
class ServiceProvider implements ServiceProviderInterface
|
||||||
{
|
{
|
||||||
|
/** @var callable[] */
|
||||||
|
protected array $factories;
|
||||||
/**
|
/**
|
||||||
* @var callable[]
|
* @var callable[]
|
||||||
*/
|
*/
|
||||||
protected $factories;
|
protected array $extensions;
|
||||||
/**
|
|
||||||
* @var callable[]
|
|
||||||
*/
|
|
||||||
protected $extensions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable[] $factories A map of service name to service factory.
|
* @param callable[] $factories A map of service name to service factory.
|
||||||
|
|
|
@ -55,6 +55,7 @@ class SimpleCacheContainer implements
|
||||||
*/
|
*/
|
||||||
public function has($id)
|
public function has($id)
|
||||||
{
|
{
|
||||||
|
$id = (string) $id;
|
||||||
$storage = $this->storage;
|
$storage = $this->storage;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -77,7 +78,11 @@ class SimpleCacheContainer implements
|
||||||
try {
|
try {
|
||||||
$storage->set($key, $value, $ttl);
|
$storage->set($key, $value, $ttl);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new ContainerException(sprintf('Could not set key "%1$s" with value "%2$s"', $key, $value), 0, $e);
|
throw new ContainerException(
|
||||||
|
sprintf('Could not set key "%1$s" with value "%2$s"', $key, (string) $value),
|
||||||
|
0,
|
||||||
|
$e
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +110,7 @@ class SimpleCacheContainer implements
|
||||||
try {
|
try {
|
||||||
$storage->clear();
|
$storage->clear();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new ContainerException(sprintf('Could not clear container'), 0, $e);
|
throw new ContainerException('Could not clear container', 0, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
121
lib/packages/Dhii/Container/TaggingServiceProvider.php
Normal file
121
lib/packages/Dhii/Container/TaggingServiceProvider.php
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Vendor\Dhii\Container;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
use ReflectionException;
|
||||||
|
use ReflectionFunction;
|
||||||
|
use ReflectionObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service provider that detects tags in factory docBlocks, and exposes them as services.
|
||||||
|
*
|
||||||
|
* A service may have a docBlock. The docBlock may contain various docBlock tags, such as `@param` or `@return`.
|
||||||
|
* This class will detect `@tag {tagname}` tags in service docBlocks. `tagname` may be anything that a service
|
||||||
|
* key may be - they exist in the same namespace. In fact, a `tagname` corresponds to a service
|
||||||
|
* that returns a list of tagged services. To retrieve them, just resolve the tagname as a service.
|
||||||
|
*
|
||||||
|
* For each unique `tagname` in factory docBlocks, this service provider will create an extension with
|
||||||
|
* an identical name. This extension at resolution time will resolve each tagged service by key,
|
||||||
|
* and add resulting services to the list it is extending. To ensure there's always a list to extend,
|
||||||
|
* this service provider will also add a service with an identical name, which resolves to an empty list.
|
||||||
|
* All such "tag" services are empty list in the beginning of their resolution, so it doesn't matter
|
||||||
|
* if it gets overwritten by another module's identical empty list.
|
||||||
|
*
|
||||||
|
* @psalm-import-type Factory from ServiceProvider
|
||||||
|
* @psalm-import-type Extension from ServiceProvider
|
||||||
|
*/
|
||||||
|
class TaggingServiceProvider implements ServiceProviderInterface
|
||||||
|
{
|
||||||
|
/** @var array<Factory> */
|
||||||
|
protected array $factories;
|
||||||
|
/** @var array<Extension> */
|
||||||
|
protected array $extensions;
|
||||||
|
|
||||||
|
public function __construct(ServiceProviderInterface $inner)
|
||||||
|
{
|
||||||
|
$this->factories = $inner->getFactories();
|
||||||
|
$this->extensions = $inner->getExtensions();
|
||||||
|
$this->indexTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getFactories()
|
||||||
|
{
|
||||||
|
return $this->factories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getExtensions()
|
||||||
|
{
|
||||||
|
return $this->extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indexes tagged factories, and creates factories and extensions for tags.
|
||||||
|
*
|
||||||
|
* @throws ReflectionException If problem obtaining factory reflection.
|
||||||
|
*/
|
||||||
|
protected function indexTags(): void
|
||||||
|
{
|
||||||
|
$tags = [];
|
||||||
|
|
||||||
|
foreach ($this->factories as $serviceName => $factory) {
|
||||||
|
if (is_string($factory)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$reflection = is_object($factory) && get_class($factory) === 'Closure'
|
||||||
|
? new ReflectionFunction($factory)
|
||||||
|
: new ReflectionObject($factory);
|
||||||
|
$docBlock = $reflection->getDocComment();
|
||||||
|
|
||||||
|
// No docblock
|
||||||
|
if ($docBlock === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$factoryTags = $this->getTagsFromDocBlock($docBlock);
|
||||||
|
foreach ($factoryTags as $tag) {
|
||||||
|
if (!isset($tags[$tag]) || !is_array($tags[$tag])) {
|
||||||
|
$tags[$tag] = [];
|
||||||
|
}
|
||||||
|
$tags[$tag][] = $serviceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tags as $tag => $taggedServiceNames) {
|
||||||
|
$this->factories[$tag] = fn (): array => [];
|
||||||
|
$this->extensions[$tag] = function (ContainerInterface $c, array $prev) use ($taggedServiceNames): array {
|
||||||
|
return array_merge(
|
||||||
|
$prev,
|
||||||
|
array_map(fn (string $serviceName) => $c->get($serviceName), $taggedServiceNames)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves tags names that are part of a docBlock.
|
||||||
|
*
|
||||||
|
* @link https://www.php.net/manual/en/reflectionclass.getdoccomment.php#118606
|
||||||
|
*
|
||||||
|
* @param string $docBlock The docBlock.
|
||||||
|
*
|
||||||
|
* @return array<string> A list of tag names.
|
||||||
|
*/
|
||||||
|
protected function getTagsFromDocBlock(string $docBlock): array
|
||||||
|
{
|
||||||
|
$regex = '#^\s*/?\**\s*(@tag\s*(?P<tags>[^\s]+))#m';
|
||||||
|
preg_match_all($regex, $docBlock, $matches);
|
||||||
|
|
||||||
|
return $matches['tags'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ trait StringTranslatingTrait
|
||||||
*/
|
*/
|
||||||
protected function __(string $string, array $args = array(), $context = null): string
|
protected function __(string $string, array $args = array(), $context = null): string
|
||||||
{
|
{
|
||||||
|
$context = (string) $context;
|
||||||
$string = $this->_translate($string, $context);
|
$string = $this->_translate($string, $context);
|
||||||
array_unshift($args, $string);
|
array_unshift($args, $string);
|
||||||
return call_user_func_array('sprintf', $args);
|
return call_user_func_array('sprintf', $args);
|
||||||
|
|
|
@ -1,52 +1,38 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerExceptionInterface;
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Service from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule
|
||||||
|
* @phpstan-import-type ExtendingService from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule
|
||||||
|
*/
|
||||||
class ContainerConfigurator
|
class ContainerConfigurator
|
||||||
{
|
{
|
||||||
/**
|
/** @var array<string, Service> */
|
||||||
* @var array<string, callable(ContainerInterface $container):mixed>
|
private array $services = [];
|
||||||
*/
|
/** @var array<string, bool> */
|
||||||
private $services = [];
|
private array $factoryIds = [];
|
||||||
|
private ServiceExtensions $extensions;
|
||||||
|
private ?ContainerInterface $compiledContainer = null;
|
||||||
|
/** @var ContainerInterface[] */
|
||||||
|
private array $containers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, bool>
|
|
||||||
*/
|
|
||||||
private $factoryIds = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, array<callable(mixed $service, ContainerInterface $container):mixed>>
|
|
||||||
*/
|
|
||||||
private $extensions = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ContainerInterface[]
|
|
||||||
*/
|
|
||||||
private $containers = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var null|ContainerInterface
|
|
||||||
*/
|
|
||||||
private $compiledContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ContainerConfigurator constructor.
|
|
||||||
*
|
|
||||||
* @param ContainerInterface[] $containers
|
* @param ContainerInterface[] $containers
|
||||||
*/
|
*/
|
||||||
public function __construct(array $containers = [])
|
public function __construct(array $containers = [], ?ServiceExtensions $extensions = null)
|
||||||
{
|
{
|
||||||
array_map([$this, 'addContainer'], $containers);
|
array_map([$this, 'addContainer'], $containers);
|
||||||
|
$this->extensions = $extensions ?? new ServiceExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allowing to add child containers.
|
|
||||||
*
|
|
||||||
* @param ContainerInterface $container
|
* @param ContainerInterface $container
|
||||||
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function addContainer(ContainerInterface $container): void
|
public function addContainer(ContainerInterface $container): void
|
||||||
{
|
{
|
||||||
|
@ -55,7 +41,7 @@ class ContainerConfigurator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param callable(ContainerInterface $container):mixed $factory
|
* @param Service $factory
|
||||||
*/
|
*/
|
||||||
public function addFactory(string $id, callable $factory): void
|
public function addFactory(string $id, callable $factory): void
|
||||||
{
|
{
|
||||||
|
@ -67,8 +53,7 @@ class ContainerConfigurator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param callable(ContainerInterface $container):mixed $service
|
* @param Service $service
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function addService(string $id, callable $service): void
|
public function addService(string $id, callable $service): void
|
||||||
|
@ -89,7 +74,6 @@ class ContainerConfigurator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasService(string $id): bool
|
public function hasService(string $id): bool
|
||||||
|
@ -109,37 +93,31 @@ class ContainerConfigurator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param callable(mixed $service, ContainerInterface $container):mixed $extender
|
* @param ExtendingService $extender
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function addExtension(string $id, callable $extender): void
|
public function addExtension(string $id, callable $extender): void
|
||||||
{
|
{
|
||||||
if (!isset($this->extensions[$id])) {
|
$this->extensions->add($id, $extender);
|
||||||
$this->extensions[$id] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->extensions[$id][] = $extender;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasExtension(string $id): bool
|
public function hasExtension(string $id): bool
|
||||||
{
|
{
|
||||||
return isset($this->extensions[$id]);
|
return $this->extensions->has($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a read only version of this Container.
|
|
||||||
*
|
|
||||||
* @return ContainerInterface
|
* @return ContainerInterface
|
||||||
|
*
|
||||||
|
* @phpstan-assert ContainerInterface $this->compiledContainer
|
||||||
*/
|
*/
|
||||||
public function createReadOnlyContainer(): ContainerInterface
|
public function createReadOnlyContainer(): ContainerInterface
|
||||||
{
|
{
|
||||||
if (!$this->compiledContainer) {
|
if ($this->compiledContainer === null) {
|
||||||
$this->compiledContainer = new ReadOnlyContainer(
|
$this->compiledContainer = new ReadOnlyContainer(
|
||||||
$this->services,
|
$this->services,
|
||||||
$this->factoryIds,
|
$this->factoryIds,
|
||||||
|
|
|
@ -10,15 +10,8 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
class PackageProxyContainer implements ContainerInterface
|
class PackageProxyContainer implements ContainerInterface
|
||||||
{
|
{
|
||||||
/**
|
private Package $package;
|
||||||
* @var Package
|
private ?ContainerInterface $container = null;
|
||||||
*/
|
|
||||||
private $package;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ContainerInterface|null
|
|
||||||
*/
|
|
||||||
private $container;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Package $package
|
* @param Package $package
|
||||||
|
@ -31,8 +24,6 @@ class PackageProxyContainer implements ContainerInterface
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function get(string $id)
|
public function get(string $id)
|
||||||
{
|
{
|
||||||
|
@ -44,8 +35,6 @@ class PackageProxyContainer implements ContainerInterface
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function has(string $id): bool
|
public function has(string $id): bool
|
||||||
{
|
{
|
||||||
|
@ -55,33 +44,30 @@ class PackageProxyContainer implements ContainerInterface
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @phpstan-assert-if-true ContainerInterface $this->container
|
||||||
* @psalm-assert-if-true ContainerInterface $this->container
|
* @phpstan-assert-if-false null $this->container
|
||||||
*/
|
*/
|
||||||
private function tryContainer(): bool
|
private function tryContainer(): bool
|
||||||
{
|
{
|
||||||
if ($this->container) {
|
if ($this->container !== null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TODO: We need a better way to deal with status checking besides equality */
|
|
||||||
if (
|
if (
|
||||||
$this->package->statusIs(Package::STATUS_READY)
|
$this->package->hasContainer()
|
||||||
|| $this->package->statusIs(Package::STATUS_BOOTED)
|
|| $this->package->hasReachedStatus(Package::STATUS_INITIALIZED)
|
||||||
) {
|
) {
|
||||||
$this->container = $this->package->container();
|
$this->container = $this->package->container();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bool)$this->container;
|
return $this->container !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @phpstan-assert ContainerInterface $this->container
|
||||||
*
|
|
||||||
* @psalm-assert ContainerInterface $this->container
|
|
||||||
*/
|
*/
|
||||||
private function assertPackageBooted(string $id): void
|
private function assertPackageBooted(string $id): void
|
||||||
{
|
{
|
||||||
|
@ -90,13 +76,11 @@ class PackageProxyContainer implements ContainerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = $this->package->name();
|
$name = $this->package->name();
|
||||||
$status = $this->package->statusIs(Package::STATUS_FAILED)
|
$status = $this->package->hasFailed() ? 'is errored' : 'is not ready yet';
|
||||||
? 'is errored'
|
|
||||||
: 'is not ready yet';
|
|
||||||
|
|
||||||
throw new class ("Error retrieving service {$id} because package {$name} {$status}.")
|
$error = "Error retrieving service {$id} because package {$name} {$status}.";
|
||||||
extends \Exception
|
throw new class (esc_html($error)) extends \Exception implements ContainerExceptionInterface
|
||||||
implements ContainerExceptionInterface {
|
{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,58 +7,43 @@ namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\NotFoundExceptionInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\NotFoundExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Service from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule
|
||||||
|
* @phpstan-import-type ExtendingService from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule
|
||||||
|
*/
|
||||||
class ReadOnlyContainer implements ContainerInterface
|
class ReadOnlyContainer implements ContainerInterface
|
||||||
{
|
{
|
||||||
/**
|
/** @var array<string, Service> */
|
||||||
* @var array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
private array $services;
|
||||||
*/
|
/** @var array<string, bool> */
|
||||||
private $services;
|
private array $factoryIds;
|
||||||
|
private ServiceExtensions $extensions;
|
||||||
|
/** @var ContainerInterface[] */
|
||||||
|
private array $containers;
|
||||||
|
/** @var array<string, mixed> */
|
||||||
|
private array $resolvedServices = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, bool>
|
* @param array<string, Service> $services
|
||||||
*/
|
|
||||||
private $factoryIds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, array<callable(mixed, ContainerInterface $container):mixed>>
|
|
||||||
*/
|
|
||||||
private $extensions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolved factories.
|
|
||||||
*
|
|
||||||
* @var array<string, mixed>
|
|
||||||
*/
|
|
||||||
private $resolvedServices = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ContainerInterface[]
|
|
||||||
*/
|
|
||||||
private $containers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ReadOnlyContainer constructor.
|
|
||||||
*
|
|
||||||
* @param array<string, callable(ContainerInterface $container):mixed> $services
|
|
||||||
* @param array<string, bool> $factoryIds
|
* @param array<string, bool> $factoryIds
|
||||||
* @param array<string, array<callable(mixed, ContainerInterface $container):mixed>> $extensions
|
* @param ServiceExtensions|array<string, ExtendingService> $extensions
|
||||||
* @param ContainerInterface[] $containers
|
* @param ContainerInterface[] $containers
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $services,
|
array $services,
|
||||||
array $factoryIds,
|
array $factoryIds,
|
||||||
array $extensions,
|
$extensions,
|
||||||
array $containers
|
array $containers
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$this->services = $services;
|
$this->services = $services;
|
||||||
$this->factoryIds = $factoryIds;
|
$this->factoryIds = $factoryIds;
|
||||||
$this->extensions = $extensions;
|
$this->extensions = $this->configureServiceExtensions($extensions);
|
||||||
$this->containers = $containers;
|
$this->containers = $containers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function get(string $id)
|
public function get(string $id)
|
||||||
|
@ -69,7 +54,7 @@ class ReadOnlyContainer implements ContainerInterface
|
||||||
|
|
||||||
if (array_key_exists($id, $this->services)) {
|
if (array_key_exists($id, $this->services)) {
|
||||||
$service = $this->services[$id]($this);
|
$service = $this->services[$id]($this);
|
||||||
$resolved = $this->resolveExtensions($id, $service);
|
$resolved = $this->extensions->resolve($service, $id, $this);
|
||||||
|
|
||||||
if (!isset($this->factoryIds[$id])) {
|
if (!isset($this->factoryIds[$id])) {
|
||||||
$this->resolvedServices[$id] = $resolved;
|
$this->resolvedServices[$id] = $resolved;
|
||||||
|
@ -83,19 +68,18 @@ class ReadOnlyContainer implements ContainerInterface
|
||||||
if ($container->has($id)) {
|
if ($container->has($id)) {
|
||||||
$service = $container->get($id);
|
$service = $container->get($id);
|
||||||
|
|
||||||
return $this->resolveExtensions($id, $service);
|
return $this->extensions->resolve($service, $id, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new class ("Service with ID {$id} not found.")
|
$error = "Service with ID {$id} not found.";
|
||||||
extends \Exception
|
throw new class (esc_html($error)) extends \Exception implements NotFoundExceptionInterface
|
||||||
implements NotFoundExceptionInterface {
|
{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function has(string $id): bool
|
public function has(string $id): bool
|
||||||
|
@ -118,21 +102,43 @@ class ReadOnlyContainer implements ContainerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* Support extensions as array or ServiceExtensions instance for backward compatibility.
|
||||||
* @param mixed $service
|
|
||||||
*
|
*
|
||||||
* @return mixed
|
* With PHP 8+ we could use an actual union type, but when we bump to PHP 8 as min supported
|
||||||
|
* version, we will probably bump major version as well, so we can just get rid of support
|
||||||
|
* for array.
|
||||||
|
*
|
||||||
|
* @param mixed $extensions
|
||||||
|
* @return ServiceExtensions
|
||||||
*/
|
*/
|
||||||
private function resolveExtensions(string $id, $service)
|
private function configureServiceExtensions($extensions): ServiceExtensions
|
||||||
{
|
{
|
||||||
if (!isset($this->extensions[$id])) {
|
if ($extensions instanceof ServiceExtensions) {
|
||||||
return $service;
|
return $extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->extensions[$id] as $extender) {
|
if (!is_array($extensions)) {
|
||||||
$service = $extender($service, $this);
|
$type = is_object($extensions) ? get_class($extensions) : gettype($extensions);
|
||||||
|
throw new \TypeError(
|
||||||
|
sprintf(
|
||||||
|
'%s::%s(): Argument #3 ($extensions) must be of type %s|array, %s given',
|
||||||
|
__CLASS__,
|
||||||
|
'__construct',
|
||||||
|
ServiceExtensions::class,
|
||||||
|
esc_html($type)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $service;
|
$servicesExtensions = new ServiceExtensions();
|
||||||
|
foreach ($extensions as $id => $callback) {
|
||||||
|
/**
|
||||||
|
* @var string $id
|
||||||
|
* @var ExtendingService $callback
|
||||||
|
*/
|
||||||
|
$servicesExtensions->add($id, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $servicesExtensions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
194
lib/packages/Inpsyde/Modularity/Container/ServiceExtensions.php
Normal file
194
lib/packages/Inpsyde/Modularity/Container/ServiceExtensions.php
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface as Container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type ExtendingService from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule
|
||||||
|
*/
|
||||||
|
class ServiceExtensions
|
||||||
|
{
|
||||||
|
private const SERVICE_TYPE_NOT_CHANGED = 1;
|
||||||
|
private const SERVICE_TYPE_CHANGED = 2;
|
||||||
|
private const SERVICE_TYPE_NOT_OBJECT = 0;
|
||||||
|
|
||||||
|
/** @var array<string, list<ExtendingService>> */
|
||||||
|
protected array $extensions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
final public static function typeId(string $type): string
|
||||||
|
{
|
||||||
|
return "@instanceof<{$type}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extensionId
|
||||||
|
* @param ExtendingService $extender
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function add(string $extensionId, callable $extender): ServiceExtensions
|
||||||
|
{
|
||||||
|
if (!isset($this->extensions[$extensionId])) {
|
||||||
|
$this->extensions[$extensionId] = [];
|
||||||
|
}
|
||||||
|
$this->extensions[$extensionId][] = $extender;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extensionId
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has(string $extensionId): bool
|
||||||
|
{
|
||||||
|
return isset($this->extensions[$extensionId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $service
|
||||||
|
* @param string $id
|
||||||
|
* @param Container $container
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
final public function resolve($service, string $id, Container $container)
|
||||||
|
{
|
||||||
|
$service = $this->resolveById($id, $service, $container);
|
||||||
|
|
||||||
|
return is_object($service)
|
||||||
|
? $this->resolveByType(get_class($service), $service, $container)
|
||||||
|
: $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @param mixed $service
|
||||||
|
* @param Container $container
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function resolveById(string $id, $service, Container $container)
|
||||||
|
{
|
||||||
|
foreach ($this->extensions[$id] ?? [] as $extender) {
|
||||||
|
$service = $extender($service, $container);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $className
|
||||||
|
* @param object $service
|
||||||
|
* @param Container $container
|
||||||
|
* @param string[] $extendedClasses
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* phpcs:disable SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
|
||||||
|
* phpcs:disable Syde.Functions.ReturnTypeDeclaration.NoReturnType
|
||||||
|
*/
|
||||||
|
protected function resolveByType(
|
||||||
|
string $className,
|
||||||
|
object $service,
|
||||||
|
Container $container,
|
||||||
|
array $extendedClasses = []
|
||||||
|
) {
|
||||||
|
// phpcs:enable SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
|
||||||
|
// phpcs:enable Syde.Functions.ReturnTypeDeclaration.NoReturnType
|
||||||
|
|
||||||
|
$extendedClasses[] = $className;
|
||||||
|
|
||||||
|
/** @var array<class-string, list<ExtendingService>> $allCallbacks */
|
||||||
|
$allCallbacks = [];
|
||||||
|
|
||||||
|
// 1st group of extensions: targeting exact class
|
||||||
|
$byClass = $this->extensions[self::typeId($className)] ?? null;
|
||||||
|
if (($byClass !== null) && ($byClass !== [])) {
|
||||||
|
$allCallbacks[$className] = $byClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd group of extensions: targeting parent classes
|
||||||
|
$parents = class_parents($service, false) ?: [];
|
||||||
|
foreach ($parents as $parentName) {
|
||||||
|
$byParent = $this->extensions[self::typeId($parentName)] ?? null;
|
||||||
|
if (($byParent !== null) && ($byParent !== [])) {
|
||||||
|
$allCallbacks[$parentName] = $byParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3rd group of extensions: targeting implemented interfaces
|
||||||
|
$interfaces = class_implements($service, false) ?: [];
|
||||||
|
foreach ($interfaces as $interfaceName) {
|
||||||
|
$byInterface = $this->extensions[self::typeId($interfaceName)] ?? null;
|
||||||
|
if (($byInterface !== null) && ($byInterface !== [])) {
|
||||||
|
$allCallbacks[$interfaceName] = $byInterface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$resultType = self::SERVICE_TYPE_NOT_CHANGED;
|
||||||
|
/** @var class-string $type */
|
||||||
|
foreach ($allCallbacks as $type => $extenders) {
|
||||||
|
// When the previous group of callbacks resulted in a type change, we need to check
|
||||||
|
// type before processing next group.
|
||||||
|
if (($resultType === self::SERVICE_TYPE_CHANGED) && !is_a($service, $type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var object $service */
|
||||||
|
[$service, $resultType] = $this->extendByType($type, $service, $container, $extenders);
|
||||||
|
if ($resultType === self::SERVICE_TYPE_NOT_OBJECT) {
|
||||||
|
// Service is not an object anymore, let's return it.
|
||||||
|
return $service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If type changed since beginning, let's start over.
|
||||||
|
// We check if class was already extended to avoid infinite recursion. E.g. instead of:
|
||||||
|
// `-> extend(A): B -> extend(B): A -> *loop* ->`
|
||||||
|
// we have:
|
||||||
|
// `-> extend(A): B -> extend(B): A -> return A`.
|
||||||
|
$newClassName = get_class($service);
|
||||||
|
if (!in_array($newClassName, $extendedClasses, true)) {
|
||||||
|
return $this->resolveByType($newClassName, $service, $container, $extendedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param class-string $type
|
||||||
|
* @param object $service
|
||||||
|
* @param Container $container
|
||||||
|
* @param list<ExtendingService> $extenders
|
||||||
|
*
|
||||||
|
* @return list{mixed, int}
|
||||||
|
*/
|
||||||
|
private function extendByType(
|
||||||
|
string $type,
|
||||||
|
object $service,
|
||||||
|
Container $container,
|
||||||
|
array $extenders
|
||||||
|
): array {
|
||||||
|
|
||||||
|
foreach ($extenders as $extender) {
|
||||||
|
$service = $extender($service, $container);
|
||||||
|
if (!is_object($service)) {
|
||||||
|
return [$service, self::SERVICE_TYPE_NOT_OBJECT];
|
||||||
|
}
|
||||||
|
if (!is_a($service, $type)) {
|
||||||
|
return [$service, self::SERVICE_TYPE_CHANGED];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$service, self::SERVICE_TYPE_NOT_CHANGED];
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
interface ExecutableModule extends Module
|
interface ExecutableModule extends Module
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform actions with objects retrieved from the container. Usually, adding WordPress hooks.
|
* Perform actions with objects retrieved from the container. Usually, adding WordPress hooks.
|
||||||
* Return true to signal a success, false to signal a failure.
|
* Return true to signal a success, false to signal a failure.
|
||||||
|
|
|
@ -4,9 +4,13 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-type ExtendingService callable(mixed $service, ContainerInterface $container): mixed
|
||||||
|
*/
|
||||||
interface ExtendingModule extends Module
|
interface ExtendingModule extends Module
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return application services' extensions.
|
* Return application services' extensions.
|
||||||
*
|
*
|
||||||
|
@ -18,7 +22,7 @@ interface ExtendingModule extends Module
|
||||||
* That is done by using as ID (array key in the `extensions` method) the target module ID
|
* That is done by using as ID (array key in the `extensions` method) the target module ID
|
||||||
* and the service ID.
|
* and the service ID.
|
||||||
*
|
*
|
||||||
* @return array<string, callable(mixed $service, \WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
* @return array<string, ExtendingService>
|
||||||
*/
|
*/
|
||||||
public function extensions(): array;
|
public function extensions(): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Service from ServiceModule
|
||||||
|
*/
|
||||||
interface FactoryModule extends Module
|
interface FactoryModule extends Module
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +15,7 @@ interface FactoryModule extends Module
|
||||||
* Similar to `services`, but object created by given factories are not "cached", but a *new*
|
* Similar to `services`, but object created by given factories are not "cached", but a *new*
|
||||||
* instance is returned everytime `get()` is called in the container.
|
* instance is returned everytime `get()` is called in the container.
|
||||||
*
|
*
|
||||||
* @return array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
* @return array<string, Service>
|
||||||
*/
|
*/
|
||||||
public function factories(): array;
|
public function factories(): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,10 @@ namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||||
*/
|
*/
|
||||||
interface Module
|
interface Module
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unique identifier for your Module.
|
* Unique identifier for your Module.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function id(): string;
|
public function id(): string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,11 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||||
|
|
||||||
/**
|
|
||||||
* Trait ModuleClassNameIdTrait
|
|
||||||
*
|
|
||||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module
|
|
||||||
*/
|
|
||||||
trait ModuleClassNameIdTrait
|
trait ModuleClassNameIdTrait
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @see Module::id()
|
* @see Module::id()
|
||||||
*/
|
*/
|
||||||
public function id(): string
|
public function id(): string
|
||||||
|
|
|
@ -4,9 +4,13 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-type Service callable(ContainerInterface $container): mixed
|
||||||
|
*/
|
||||||
interface ServiceModule extends Module
|
interface ServiceModule extends Module
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return application services' factories.
|
* Return application services' factories.
|
||||||
*
|
*
|
||||||
|
@ -15,7 +19,7 @@ interface ServiceModule extends Module
|
||||||
* Services are "cached", so the given factory is called once the first time `get()` is called
|
* Services are "cached", so the given factory is called once the first time `get()` is called
|
||||||
* in the container, and on subsequent `get()` the same instance is returned again and again.
|
* in the container, and on subsequent `get()` the same instance is returned again and again.
|
||||||
*
|
*
|
||||||
* @return array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
* @return array<string, Service>
|
||||||
*/
|
*/
|
||||||
public function services(): array;
|
public function services(): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,22 @@ namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\ContainerConfigurator;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\ContainerConfigurator;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\PackageProxyContainer;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\PackageProxyContainer;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\Module;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\Module;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties\Properties;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties\Properties;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Service from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule
|
||||||
|
* @phpstan-import-type ExtendingService from \WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule
|
||||||
|
*/
|
||||||
class Package
|
class Package
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* All the hooks fired in this class use this prefix.
|
* All the hooks fired in this class use this prefix.
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
private const HOOK_PREFIX = 'inpsyde.modularity.';
|
private const HOOK_PREFIX = 'inpsyde.modularity.';
|
||||||
|
|
||||||
|
@ -34,14 +37,13 @@ class Package
|
||||||
* $container->has(Package::PROPERTIES);
|
* $container->has(Package::PROPERTIES);
|
||||||
* $container->get(Package::PROPERTIES);
|
* $container->get(Package::PROPERTIES);
|
||||||
* </code>
|
* </code>
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
public const PROPERTIES = 'properties';
|
public const PROPERTIES = 'properties';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action to be used to add Modules to the package.
|
* Custom action to be used to add modules and connect other packages.
|
||||||
* It might also be used to access package properties.
|
* It might also be used to access package properties.
|
||||||
|
* Access container is not possible at this stage.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* <code>
|
* <code>
|
||||||
|
@ -49,67 +51,64 @@ class Package
|
||||||
*
|
*
|
||||||
* add_action(
|
* add_action(
|
||||||
* $package->hookName(Package::ACTION_INIT),
|
* $package->hookName(Package::ACTION_INIT),
|
||||||
* $callback
|
* fn (Package $package) => // do something,
|
||||||
* );
|
* );
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public const ACTION_INIT = 'init';
|
public const ACTION_INIT = 'init';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action which is triggered after the application
|
* Very similar to `ACTION_INIT`, but it is static, so not dependent on package name.
|
||||||
* is booted to access container and properties.
|
* It passes package name as first argument.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* <code>
|
* <code>
|
||||||
* $package = Package::new();
|
* add_action(
|
||||||
*
|
* Package::ACTION_MODULARITY_INIT,
|
||||||
* add_action(
|
* fn (string $packageName, Package $package) => // do something,
|
||||||
* $package->hookName(Package::ACTION_READY),
|
* 10,
|
||||||
* $callback
|
* 2
|
||||||
* );
|
* );
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public const ACTION_READY = 'ready';
|
public const ACTION_MODULARITY_INIT = self::HOOK_PREFIX . self::ACTION_INIT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action which is triggered when a failure happens during the building stage.
|
* Action fired when it is safe to access container.
|
||||||
*
|
* Add more modules is not anymore possible at this stage.
|
||||||
* @example
|
*/
|
||||||
* <code>
|
public const ACTION_INITIALIZED = 'initialized';
|
||||||
* $package = Package::new();
|
|
||||||
*
|
/**
|
||||||
* add_action(
|
* Action fired when plugin finished its bootstrapping process, all its hooks are added.
|
||||||
* $package->hookName(Package::ACTION_FAILED_BUILD),
|
* Add more modules is not anymore possible at this stage.
|
||||||
* $callback
|
*/
|
||||||
* );
|
public const ACTION_BOOTED = 'ready';
|
||||||
* </code>
|
|
||||||
|
/**
|
||||||
|
* Action fired when anything went wrong during the "build" procedure.
|
||||||
*/
|
*/
|
||||||
public const ACTION_FAILED_BUILD = 'failed-build';
|
public const ACTION_FAILED_BUILD = 'failed-build';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action which is triggered when a failure happens during the booting stage.
|
* Action fired when anything went wrong during the "boot" procedure.
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* <code>
|
|
||||||
* $package = Package::new();
|
|
||||||
*
|
|
||||||
* add_action(
|
|
||||||
* $package->hookName(Package::ACTION_FAILED_BOOT),
|
|
||||||
* $callback
|
|
||||||
* );
|
|
||||||
* </code>
|
|
||||||
*/
|
*/
|
||||||
public const ACTION_FAILED_BOOT = 'failed-boot';
|
public const ACTION_FAILED_BOOT = 'failed-boot';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action which is triggered when a package is connected.
|
* Action fired when adding a module failed.
|
||||||
*/
|
*/
|
||||||
public const ACTION_PACKAGE_CONNECTED = 'package-connected';
|
public const ACTION_FAILED_ADD_MODULE = 'failed-add-module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom action which is triggered when a package cannot be connected.
|
* Action fired when a package connection failed.
|
||||||
*/
|
*/
|
||||||
public const ACTION_FAILED_CONNECTION = 'failed-connection';
|
public const ACTION_FAILED_CONNECT = 'failed-connection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action fired when a package is connected successfully.
|
||||||
|
*/
|
||||||
|
public const ACTION_PACKAGE_CONNECTED = 'package-connected';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module states can be used to get information about your module.
|
* Module states can be used to get information about your module.
|
||||||
|
@ -118,7 +117,7 @@ class Package
|
||||||
* <code>
|
* <code>
|
||||||
* $package = Package::new();
|
* $package = Package::new();
|
||||||
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // false
|
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // false
|
||||||
* $package->boot(new SomeModule());
|
* $package->addModule(new SomeModule());
|
||||||
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // true
|
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // true
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
|
@ -137,90 +136,81 @@ class Package
|
||||||
* @example
|
* @example
|
||||||
* <code>
|
* <code>
|
||||||
* $package = Package::new();
|
* $package = Package::new();
|
||||||
* $package->statusIs(Package::IDLE); // true
|
* $package->statusIs(Package::STATUS_IDLE); // true
|
||||||
|
* $package->build();
|
||||||
|
* $package->statusIs(Package::STATUS_INITIALIZED); // true
|
||||||
* $package->boot();
|
* $package->boot();
|
||||||
* $package->statusIs(Package::BOOTED); // true
|
* $package->statusIs(Package::STATUS_DONE); // true
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public const STATUS_IDLE = 2;
|
public const STATUS_IDLE = 2;
|
||||||
|
public const STATUS_INITIALIZING = 3;
|
||||||
public const STATUS_INITIALIZED = 4;
|
public const STATUS_INITIALIZED = 4;
|
||||||
public const STATUS_MODULES_ADDED = 5;
|
public const STATUS_BOOTING = 5;
|
||||||
public const STATUS_READY = 7;
|
public const STATUS_BOOTED = 7;
|
||||||
public const STATUS_BOOTED = 8;
|
public const STATUS_DONE = 8;
|
||||||
public const STATUS_FAILED = -8;
|
public const STATUS_FAILED = -8;
|
||||||
|
|
||||||
/**
|
// Deprecated flags
|
||||||
* Current state of the application.
|
/** @deprecated */
|
||||||
*
|
public const STATUS_MODULES_ADDED = self::STATUS_BOOTING;
|
||||||
* @see Package::STATUS_*
|
/** @deprecated */
|
||||||
*
|
public const ACTION_READY = self::ACTION_BOOTED;
|
||||||
* @var int
|
/** @deprecated */
|
||||||
*/
|
public const ACTION_FAILED_CONNECTION = self::ACTION_FAILED_CONNECT;
|
||||||
private $status = self::STATUS_IDLE;
|
|
||||||
|
|
||||||
/**
|
// Map of status to package-specific and global hook, both optional (i..e, null).
|
||||||
* Contains the progress of all modules.
|
private const STATUSES_ACTIONS_MAP = [
|
||||||
*
|
self::STATUS_INITIALIZING => [self::ACTION_INIT, self::ACTION_MODULARITY_INIT],
|
||||||
* @see Package::moduleProgress()
|
self::STATUS_INITIALIZED => [self::ACTION_INITIALIZED, null],
|
||||||
*
|
self::STATUS_BOOTED => [self::ACTION_BOOTED, null],
|
||||||
* @var array<string, list<string>>
|
];
|
||||||
*/
|
|
||||||
private $moduleStatus = [self::MODULES_ALL => []];
|
|
||||||
|
|
||||||
/**
|
private const SUCCESS_STATUSES = [
|
||||||
* Hashmap of where keys are names of connected packages, and values are boolean, true
|
self::STATUS_IDLE => self::STATUS_IDLE,
|
||||||
* if connection was successful.
|
self::STATUS_INITIALIZING => self::STATUS_INITIALIZING,
|
||||||
*
|
self::STATUS_INITIALIZED => self::STATUS_INITIALIZED,
|
||||||
* @see Package::connect()
|
self::STATUS_BOOTING => self::STATUS_BOOTING,
|
||||||
*
|
self::STATUS_BOOTED => self::STATUS_BOOTED,
|
||||||
* @var array<string, bool>
|
self::STATUS_DONE => self::STATUS_DONE,
|
||||||
*/
|
];
|
||||||
private $connectedPackages = [];
|
|
||||||
|
|
||||||
/**
|
private const OPERATORS = [
|
||||||
* @var list<ExecutableModule>
|
'<' => '<',
|
||||||
*/
|
'<=' => '<=',
|
||||||
private $executables = [];
|
'>' => '>',
|
||||||
|
'>=' => '>=',
|
||||||
|
'==' => '==',
|
||||||
|
'!=' => '!=',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/** @var Package::STATUS_* */
|
||||||
* @var Properties
|
private int $status = self::STATUS_IDLE;
|
||||||
*/
|
/** @var array<string, list<string>> */
|
||||||
private $properties;
|
private array $moduleStatus = [self::MODULES_ALL => []];
|
||||||
|
/** @var array<string, bool> */
|
||||||
/**
|
private array $connectedPackages = [];
|
||||||
* @var ContainerConfigurator
|
/** @var list<ExecutableModule> */
|
||||||
*/
|
private array $executables = [];
|
||||||
private $containerConfigurator;
|
private Properties $properties;
|
||||||
|
private ContainerConfigurator $containerConfigurator;
|
||||||
/**
|
private bool $built = false;
|
||||||
* @var bool
|
private bool $hasContainer = false;
|
||||||
*/
|
private ?\Throwable $lastError = null;
|
||||||
private $built = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $hasContainer = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Throwable|null
|
|
||||||
*/
|
|
||||||
private $lastError = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Properties $properties
|
* @param Properties $properties
|
||||||
* @param ContainerInterface[] $containers
|
* @param ContainerInterface ...$containers
|
||||||
*
|
|
||||||
* @return Package
|
* @return Package
|
||||||
*/
|
*/
|
||||||
public static function new(Properties $properties, ContainerInterface ...$containers): Package
|
public static function new(Properties $properties, ContainerInterface ...$containers): Package
|
||||||
{
|
{
|
||||||
return new self($properties, ...$containers);
|
return new self($properties, ...$containers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Properties $properties
|
* @param Properties $properties
|
||||||
* @param ContainerInterface[] $containers
|
* @param ContainerInterface ...$containers
|
||||||
*/
|
*/
|
||||||
private function __construct(Properties $properties, ContainerInterface ...$containers)
|
private function __construct(Properties $properties, ContainerInterface ...$containers)
|
||||||
{
|
{
|
||||||
|
@ -229,7 +219,7 @@ class Package
|
||||||
$this->containerConfigurator = new ContainerConfigurator($containers);
|
$this->containerConfigurator = new ContainerConfigurator($containers);
|
||||||
$this->containerConfigurator->addService(
|
$this->containerConfigurator->addService(
|
||||||
self::PROPERTIES,
|
self::PROPERTIES,
|
||||||
static function () use ($properties) {
|
static function () use ($properties): Properties {
|
||||||
return $properties;
|
return $properties;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -237,14 +227,14 @@ class Package
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Module $module
|
* @param Module $module
|
||||||
*
|
|
||||||
* @return static
|
* @return static
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function addModule(Module $module): Package
|
public function addModule(Module $module): Package
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->assertStatus(self::STATUS_IDLE, sprintf('add module %s', $module->id()));
|
$reason = sprintf('add module %s', $module->id());
|
||||||
|
$this->assertStatus(self::STATUS_FAILED, $reason, '!=');
|
||||||
|
$this->assertStatus(self::STATUS_INITIALIZING, $reason, '<=');
|
||||||
|
|
||||||
$registeredServices = $this->addModuleServices(
|
$registeredServices = $this->addModuleServices(
|
||||||
$module,
|
$module,
|
||||||
|
@ -271,7 +261,7 @@ class Package
|
||||||
$status = $added ? self::MODULE_ADDED : self::MODULE_NOT_ADDED;
|
$status = $added ? self::MODULE_ADDED : self::MODULE_NOT_ADDED;
|
||||||
$this->moduleProgress($module->id(), $status);
|
$this->moduleProgress($module->id(), $status);
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
$this->handleFailure($throwable, self::ACTION_FAILED_ADD_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -280,7 +270,6 @@ class Package
|
||||||
/**
|
/**
|
||||||
* @param Package $package
|
* @param Package $package
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function connect(Package $package): bool
|
public function connect(Package $package): bool
|
||||||
{
|
{
|
||||||
|
@ -290,33 +279,17 @@ class Package
|
||||||
}
|
}
|
||||||
|
|
||||||
$packageName = $package->name();
|
$packageName = $package->name();
|
||||||
$errorData = ['package' => $packageName, 'status' => $this->status];
|
|
||||||
$errorMessage = "Failed connecting package {$packageName}";
|
|
||||||
|
|
||||||
// Don't connect, if already connected
|
// Don't connect, if already connected
|
||||||
if (array_key_exists($packageName, $this->connectedPackages)) {
|
if (array_key_exists($packageName, $this->connectedPackages)) {
|
||||||
$error = "{$errorMessage} because it was already connected.";
|
return $this->handleConnectionFailure($packageName, 'already connected', false);
|
||||||
do_action(
|
|
||||||
$this->hookName(self::ACTION_FAILED_CONNECTION),
|
|
||||||
$packageName,
|
|
||||||
new \WP_Error('already_connected', $error, $errorData)
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new \Exception($error, 0, $this->lastError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't connect, if already booted or boot failed
|
// Don't connect, if already booted or boot failed
|
||||||
$failed = $this->statusIs(self::STATUS_FAILED);
|
$failed = $this->hasFailed();
|
||||||
if ($failed || $this->statusIs(self::STATUS_BOOTED)) {
|
if ($failed || $this->hasReachedStatus(self::STATUS_INITIALIZED)) {
|
||||||
$status = $failed ? 'errored' : 'booted';
|
$reason = $failed ? 'is errored' : 'has a built container already';
|
||||||
$error = "{$errorMessage} to a {$status} package.";
|
$this->handleConnectionFailure($packageName, "current package {$reason}", true);
|
||||||
do_action(
|
|
||||||
$this->hookName(self::ACTION_FAILED_CONNECTION),
|
|
||||||
$packageName,
|
|
||||||
new \WP_Error("no_connect_on_{$status}", $error, $errorData)
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new \Exception($error, 0, $this->lastError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->connectedPackages[$packageName] = true;
|
$this->connectedPackages[$packageName] = true;
|
||||||
|
@ -330,9 +303,10 @@ class Package
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// If the other package is booted, we can obtain a container, otherwise
|
// If we can obtain a container we do, otherwise we build a proxy container
|
||||||
// we build a proxy container
|
$packageHasContainer = $package->hasReachedStatus(self::STATUS_INITIALIZED)
|
||||||
$container = $package->statusIs(self::STATUS_BOOTED)
|
|| $package->hasContainer();
|
||||||
|
$container = $packageHasContainer
|
||||||
? $package->container()
|
? $package->container()
|
||||||
: new PackageProxyContainer($package);
|
: new PackageProxyContainer($package);
|
||||||
|
|
||||||
|
@ -347,7 +321,10 @@ class Package
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
if (isset($packageName)) {
|
if (
|
||||||
|
isset($packageName)
|
||||||
|
&& (($this->connectedPackages[$packageName] ?? false) !== true)
|
||||||
|
) {
|
||||||
$this->connectedPackages[$packageName] = false;
|
$this->connectedPackages[$packageName] = false;
|
||||||
}
|
}
|
||||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
||||||
|
@ -362,17 +339,26 @@ class Package
|
||||||
public function build(): Package
|
public function build(): Package
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Don't allow building the application multiple times.
|
// Be tolerant about things like `$package->build()->build()`.
|
||||||
|
// Sometimes, from the extern, we might want to call `build()` to ensure the container
|
||||||
|
// is ready before accessing a service. And in that case we don't want to throw an
|
||||||
|
// exception if the container is already built.
|
||||||
|
if ($this->built && $this->statusIs(self::STATUS_INITIALIZED)) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect `build` to be called only after `addModule()` or `connect()` which do
|
||||||
|
// not change the status, so we expect status to be still "IDLE".
|
||||||
|
// This will prevent invalid things like calling `build()` from inside something
|
||||||
|
// hooking ACTION_INIT OR ACTION_INITIALIZED.
|
||||||
$this->assertStatus(self::STATUS_IDLE, 'build package');
|
$this->assertStatus(self::STATUS_IDLE, 'build package');
|
||||||
|
|
||||||
do_action(
|
// This will change the status to "INITIALIZING" then fire the action that allow other
|
||||||
$this->hookName(self::ACTION_INIT),
|
// packages to add modules or connect packages.
|
||||||
$this
|
$this->progress(self::STATUS_INITIALIZING);
|
||||||
);
|
|
||||||
// Changing the status here ensures we can not call this method again, and also we can not
|
// This will change the status to "INITIALIZED" then fire an action when it is safe to
|
||||||
// add new modules, because both this and `addModule()` methods check for idle status.
|
// access the container, because from this moment on, container is locked from change.
|
||||||
// For backward compatibility, adding new modules via `boot()` will still be possible, even
|
|
||||||
// if deprecated, at the condition that the container was not yet accessed at that point.
|
|
||||||
$this->progress(self::STATUS_INITIALIZED);
|
$this->progress(self::STATUS_INITIALIZED);
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
||||||
|
@ -386,37 +372,47 @@ class Package
|
||||||
/**
|
/**
|
||||||
* @param Module ...$defaultModules Deprecated, use `addModule()` to add default modules.
|
* @param Module ...$defaultModules Deprecated, use `addModule()` to add default modules.
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
*/
|
||||||
public function boot(Module ...$defaultModules): bool
|
public function boot(Module ...$defaultModules): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// When package is done, nothing should happen to it calling boot again, but we call
|
||||||
|
// false to signal something is off.
|
||||||
|
if ($this->statusIs(self::STATUS_DONE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Call build() if not called yet, and ensure any new module passed here is added
|
// Call build() if not called yet, and ensure any new module passed here is added
|
||||||
// as well, throwing if the container was already built.
|
// as well, throwing if the container was already built.
|
||||||
$this->doBuild(...$defaultModules);
|
$this->doBuild(...$defaultModules);
|
||||||
|
|
||||||
// Don't allow booting the application multiple times.
|
// Make sure we call boot() on a non-failed instance, and also make a sanity check
|
||||||
$this->assertStatus(self::STATUS_MODULES_ADDED, 'boot application', '<');
|
// on the status flow, e.g. prevent calling boot() from an action hook.
|
||||||
$this->assertStatus(self::STATUS_FAILED, 'boot application', '!=');
|
$this->assertStatus(self::STATUS_INITIALIZED, 'boot application');
|
||||||
|
|
||||||
$this->progress(self::STATUS_MODULES_ADDED);
|
// This will change status to STATUS_BOOTING "locking" subsequent call to `boot()`, but
|
||||||
|
// no hook is fired here, because at this point we can not do anything more or less than
|
||||||
|
// what can be done on the ACTION_INITIALIZED hook, so that hook is sufficient.
|
||||||
|
$this->progress(self::STATUS_BOOTING);
|
||||||
|
|
||||||
$this->doExecute();
|
$this->doExecute();
|
||||||
|
|
||||||
$this->progress(self::STATUS_READY);
|
// This will change status to STATUS_BOOTED and then fire an action that make it
|
||||||
|
// possible to hook on a package that has finished its bootstrapping process, so all its
|
||||||
do_action(
|
// "executable" modules have been executed.
|
||||||
$this->hookName(self::ACTION_READY),
|
$this->progress(self::STATUS_BOOTED);
|
||||||
$this
|
|
||||||
);
|
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$this->handleFailure($throwable, self::ACTION_FAILED_BOOT);
|
$this->handleFailure($throwable, self::ACTION_FAILED_BOOT);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->progress(self::STATUS_BOOTED);
|
// This will change the status to DONE and will not fire any action.
|
||||||
|
// This is a status that proves that everything went well, not only the Package itself,
|
||||||
|
// but also anything hooking Package's hooks.
|
||||||
|
// The only way to move out of this status is a failure that might only happen directly
|
||||||
|
// calling `addModule()`, `connect()` or `build()`.
|
||||||
|
$this->progress(self::STATUS_DONE);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -439,39 +435,67 @@ class Package
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We expect `boot()` to be called either:
|
||||||
|
// 1. Directly after `addModule()`/`connect()`, without any `build()` call in between, so
|
||||||
|
// status is IDLE and `$this->built` is `false`.
|
||||||
|
// 2. After `build()` is called, so status is INITIALIZED and `$this->built` is `true`.
|
||||||
|
// Any other usage is not allowed (e.g. calling `boot()` from an hook callback) and in that
|
||||||
|
// case we return here, giving back control to `boot()` which will throw.
|
||||||
|
$validFlows = (!$this->built && $this->statusIs(self::STATUS_IDLE))
|
||||||
|
|| ($this->built && $this->statusIs(self::STATUS_INITIALIZED));
|
||||||
|
|
||||||
|
if (!$validFlows) {
|
||||||
|
// If none of the two supported flows happened, we just return handling control back
|
||||||
|
// to `boot()`, that will throw.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->built) {
|
if (!$this->built) {
|
||||||
array_map([$this, 'addModule'], $defaultModules);
|
// First valid flow: `boot()` was called directly after `addModule()`/`connect()`
|
||||||
|
// without any call to `build()`. We can call `build()` and return, handing control
|
||||||
|
// back to `boot()`. Before returning, if we had default modules passed to `boot()` we
|
||||||
|
// already have fired a deprecation, so here we just add them dealing with back-compat.
|
||||||
|
foreach ($defaultModules as $defaultModule) {
|
||||||
|
$this->addModule($defaultModule);
|
||||||
|
}
|
||||||
$this->build();
|
$this->build();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
// Second valid flow: we have called `boot()` after `build()`. If we did it correctly,
|
||||||
!$defaultModules
|
// without default modules passed to `boot()`, we can just return handing control back
|
||||||
|| ($this->status >= self::STATUS_MODULES_ADDED)
|
// to `boot()`.
|
||||||
|| ($this->statusIs(self::STATUS_FAILED))
|
if (!$defaultModules) {
|
||||||
) {
|
|
||||||
// if we don't have default modules, there's nothing to do, and if the status is beyond
|
|
||||||
// "modules added" or is failed, we do nothing as well and let `boot()` throw.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If here, we have done something like: `$package->build()->boot($module1, $module2)`.
|
||||||
|
// Passing modules to `boot()` was deprecated when `build()` was introduced, so whoever
|
||||||
|
// added `build()` should have removed modules passed to `boot()`.
|
||||||
|
// But we want to keep 100% backward compatibility so we still support this behavior
|
||||||
|
// until the next major is released. To do that, we simulate IDLE status to prevent
|
||||||
|
// `addModule()` from throwing when adding default modules.
|
||||||
|
// But we can do that only if we don't have a compiled container yet.
|
||||||
|
// If anything hooking ACTION_INIT called `container()` we have a compiled container
|
||||||
|
// already, and we can't add modules, so we not going to simulate INIT status, which mean
|
||||||
|
// the `$this->addModule()` call below will throw.
|
||||||
$backup = $this->status;
|
$backup = $this->status;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// simulate idle status to prevent `addModule()` from throwing
|
if (!$this->hasContainer()) {
|
||||||
// only if we don't have a container yet
|
$this->status = self::STATUS_IDLE;
|
||||||
$this->hasContainer or $this->status = self::STATUS_IDLE;
|
}
|
||||||
|
|
||||||
foreach ($defaultModules as $defaultModule) {
|
foreach ($defaultModules as $defaultModule) {
|
||||||
// If a module was added by `build()` or `addModule()` we can skip it, a
|
// If a module was already added via `addModule()` we can skip it, reducing the
|
||||||
// deprecation was trigger to make it noticeable without breakage
|
// chances of throwing an exception if not needed.
|
||||||
if (!$this->moduleIs($defaultModule->id(), self::MODULE_ADDED)) {
|
if (!$this->moduleIs($defaultModule->id(), self::MODULE_ADDED)) {
|
||||||
$this->addModule($defaultModule);
|
$this->addModule($defaultModule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
$this->status = $backup;
|
if (!$this->hasFailed()) {
|
||||||
|
$this->status = $backup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +506,9 @@ class Package
|
||||||
*/
|
*/
|
||||||
private function addModuleServices(Module $module, string $status): bool
|
private function addModuleServices(Module $module, string $status): bool
|
||||||
{
|
{
|
||||||
|
/** @var null|array<string, Service|ExtendingService> $services */
|
||||||
$services = null;
|
$services = null;
|
||||||
|
/** @var null|callable(string, Service|ExtendingService): void $addCallback */
|
||||||
$addCallback = null;
|
$addCallback = null;
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case self::MODULE_REGISTERED:
|
case self::MODULE_REGISTERED:
|
||||||
|
@ -499,21 +525,16 @@ class Package
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$services) {
|
if (($services === null) || ($services === []) || ($addCallback === null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = [];
|
$ids = [];
|
||||||
array_walk(
|
foreach ($services as $id => $service) {
|
||||||
$services,
|
$addCallback($id, $service);
|
||||||
static function (callable $service, string $id) use ($addCallback, &$ids) {
|
$ids[] = $id;
|
||||||
/** @var callable(string, callable) $addCallback */
|
}
|
||||||
$addCallback($id, $service);
|
|
||||||
/** @var list<string> $ids */
|
|
||||||
$ids[] = $id;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/** @var list<string> $ids */
|
|
||||||
$this->moduleProgress($module->id(), $status, $ids);
|
$this->moduleProgress($module->id(), $status, $ids);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -521,8 +542,6 @@ class Package
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
*/
|
||||||
private function doExecute(): void
|
private function doExecute(): void
|
||||||
{
|
{
|
||||||
|
@ -530,9 +549,7 @@ class Package
|
||||||
$success = $executable->run($this->container());
|
$success = $executable->run($this->container());
|
||||||
$this->moduleProgress(
|
$this->moduleProgress(
|
||||||
$executable->id(),
|
$executable->id(),
|
||||||
$success
|
$success ? self::MODULE_EXECUTED : self::MODULE_EXECUTION_FAILED,
|
||||||
? self::MODULE_EXECUTED
|
|
||||||
: self::MODULE_EXECUTION_FAILED
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,15 +558,20 @@ class Package
|
||||||
* @param string $moduleId
|
* @param string $moduleId
|
||||||
* @param string $status
|
* @param string $status
|
||||||
* @param list<string>|null $serviceIds
|
* @param list<string>|null $serviceIds
|
||||||
*
|
* @return void
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
private function moduleProgress(string $moduleId, string $status, ?array $serviceIds = null)
|
private function moduleProgress(
|
||||||
{
|
string $moduleId,
|
||||||
isset($this->moduleStatus[$status]) or $this->moduleStatus[$status] = [];
|
string $status,
|
||||||
|
?array $serviceIds = null
|
||||||
|
): void {
|
||||||
|
|
||||||
|
if (!isset($this->moduleStatus[$status])) {
|
||||||
|
$this->moduleStatus[$status] = [];
|
||||||
|
}
|
||||||
$this->moduleStatus[$status][] = $moduleId;
|
$this->moduleStatus[$status][] = $moduleId;
|
||||||
|
|
||||||
if (!$serviceIds || !$this->properties->isDebug()) {
|
if (($serviceIds === null) || ($serviceIds === []) || !$this->properties->isDebug()) {
|
||||||
$this->moduleStatus[self::MODULES_ALL][] = "{$moduleId} {$status}";
|
$this->moduleStatus[self::MODULES_ALL][] = "{$moduleId} {$status}";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -605,10 +627,9 @@ class Package
|
||||||
* `inpsyde.modularity.my-plugin` anyway, so the file name is not relevant.
|
* `inpsyde.modularity.my-plugin` anyway, so the file name is not relevant.
|
||||||
*
|
*
|
||||||
* @param string $suffix
|
* @param string $suffix
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
* @see Package::name()
|
|
||||||
*
|
*
|
||||||
|
* @see Package::name()
|
||||||
*/
|
*/
|
||||||
public function hookName(string $suffix = ''): string
|
public function hookName(string $suffix = ''): string
|
||||||
{
|
{
|
||||||
|
@ -631,8 +652,6 @@ class Package
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ContainerInterface
|
* @return ContainerInterface
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function container(): ContainerInterface
|
public function container(): ContainerInterface
|
||||||
{
|
{
|
||||||
|
@ -642,6 +661,14 @@ class Package
|
||||||
return $this->containerConfigurator->createReadOnlyContainer();
|
return $this->containerConfigurator->createReadOnlyContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasContainer(): bool
|
||||||
|
{
|
||||||
|
return $this->hasContainer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -652,27 +679,94 @@ class Package
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $status
|
* @param int $status
|
||||||
*/
|
|
||||||
private function progress(int $status): void
|
|
||||||
{
|
|
||||||
$this->status = $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $status
|
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function statusIs(int $status): bool
|
public function statusIs(int $status): bool
|
||||||
{
|
{
|
||||||
return $this->status === $status;
|
return $this->checkStatus($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasFailed(): bool
|
||||||
|
{
|
||||||
|
return $this->status === self::STATUS_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $status
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasReachedStatus(int $status): bool
|
||||||
|
{
|
||||||
|
if ($this->hasFailed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset(self::SUCCESS_STATUSES[$status]) && $this->checkStatus($status, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $status
|
||||||
|
* @param value-of<Package::OPERATORS> $operator
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function checkStatus(int $status, string $operator = '=='): bool
|
||||||
|
{
|
||||||
|
assert(isset(self::OPERATORS[$operator]));
|
||||||
|
|
||||||
|
return version_compare((string) $this->status, (string) $status, $operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Package::STATUS_* $status
|
||||||
|
*/
|
||||||
|
private function progress(int $status): void
|
||||||
|
{
|
||||||
|
$this->status = $status;
|
||||||
|
|
||||||
|
[$packageHookSuffix, $globalHook] = self::STATUSES_ACTIONS_MAP[$status] ?? [null, null];
|
||||||
|
if ($packageHookSuffix !== null) {
|
||||||
|
do_action($this->hookName($packageHookSuffix), $this);
|
||||||
|
}
|
||||||
|
if ($globalHook !== null) {
|
||||||
|
do_action($globalHook, $this->name(), $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string $reason
|
||||||
|
* @param bool $throw
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function handleConnectionFailure(string $packageName, string $reason, bool $throw): bool
|
||||||
|
{
|
||||||
|
$errorData = ['package' => $packageName, 'status' => $this->status];
|
||||||
|
$message = "Failed connecting package {$packageName} because {$reason}.";
|
||||||
|
|
||||||
|
do_action(
|
||||||
|
$this->hookName(self::ACTION_FAILED_CONNECT),
|
||||||
|
$packageName,
|
||||||
|
new \WP_Error('failed_connection', $message, $errorData)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($throw) {
|
||||||
|
throw new \Exception(
|
||||||
|
esc_html($message),
|
||||||
|
0,
|
||||||
|
$this->lastError // phpcs:ignore WordPress.Security.EscapeOutput
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Throwable $throwable
|
* @param \Throwable $throwable
|
||||||
* @param Package::ACTION_FAILED_* $action
|
* @param Package::ACTION_FAILED_* $action
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
*/
|
||||||
private function handleFailure(\Throwable $throwable, string $action): void
|
private function handleFailure(\Throwable $throwable, string $action): void
|
||||||
{
|
{
|
||||||
|
@ -690,18 +784,15 @@ class Package
|
||||||
/**
|
/**
|
||||||
* @param int $status
|
* @param int $status
|
||||||
* @param string $action
|
* @param string $action
|
||||||
* @param string $operator
|
* @param value-of<Package::OPERATORS> $operator
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
* @psalm-suppress ArgumentTypeCoercion
|
|
||||||
*/
|
*/
|
||||||
private function assertStatus(int $status, string $action, string $operator = '=='): void
|
private function assertStatus(int $status, string $action, string $operator = '=='): void
|
||||||
{
|
{
|
||||||
if (!version_compare((string) $this->status, (string) $status, $operator)) {
|
if (!$this->checkStatus($status, $operator)) {
|
||||||
throw new \Exception(
|
throw new \Exception(
|
||||||
sprintf("Can't %s at this point of application.", $action),
|
sprintf("Can't %s at this point of application.", esc_html($action)),
|
||||||
0,
|
0,
|
||||||
$this->lastError
|
$this->lastError // phpcs:ignore WordPress.Security.EscapeOutput
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,7 +804,6 @@ class Package
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param string $function
|
* @param string $function
|
||||||
* @param string $version
|
* @param string $version
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function deprecatedArgument(string $message, string $function, string $version): void
|
private function deprecatedArgument(string $message, string $function, string $version): void
|
||||||
|
@ -721,7 +811,9 @@ class Package
|
||||||
do_action('deprecated_argument_run', $function, $message, $version);
|
do_action('deprecated_argument_run', $function, $message, $version);
|
||||||
|
|
||||||
if (apply_filters('deprecated_argument_trigger_error', true)) {
|
if (apply_filters('deprecated_argument_trigger_error', true)) {
|
||||||
trigger_error($message, \E_USER_DEPRECATED);
|
do_action('wp_trigger_error_run', $function, $message, \E_USER_DEPRECATED);
|
||||||
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||||
|
trigger_error(esc_html($message), \E_USER_DEPRECATED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,47 +6,30 @@ namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||||
|
|
||||||
class BaseProperties implements Properties
|
class BaseProperties implements Properties
|
||||||
{
|
{
|
||||||
/**
|
protected ?bool $isDebug = null;
|
||||||
* @var null|bool
|
protected string $baseName;
|
||||||
*/
|
protected string $basePath;
|
||||||
protected $isDebug = null;
|
protected ?string $baseUrl;
|
||||||
|
/** @var array<string, mixed> */
|
||||||
/**
|
protected array $properties;
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $baseName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $basePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
protected $baseUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $properties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $baseName
|
* @param string $baseName
|
||||||
* @param string $basePath
|
* @param string $basePath
|
||||||
* @param string|null $baseUrl
|
* @param string|null $baseUrl
|
||||||
* @param array $properties
|
* @param array<string, mixed> $properties
|
||||||
*/
|
*/
|
||||||
protected function __construct(
|
protected function __construct(
|
||||||
string $baseName,
|
string $baseName,
|
||||||
string $basePath,
|
string $basePath,
|
||||||
string $baseUrl = null,
|
?string $baseUrl = null,
|
||||||
array $properties = []
|
array $properties = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$baseName = $this->sanitizeBaseName($baseName);
|
$baseName = $this->sanitizeBaseName($baseName);
|
||||||
$basePath = (string) trailingslashit($basePath);
|
$basePath = trailingslashit($basePath);
|
||||||
if ($baseUrl) {
|
if ($baseUrl !== null) {
|
||||||
$baseUrl = (string) trailingslashit($baseUrl);
|
$baseUrl = trailingslashit($baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->baseName = $baseName;
|
$this->baseName = $baseName;
|
||||||
|
@ -58,11 +41,13 @@ class BaseProperties implements Properties
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @return string
|
* @return lowercase-string
|
||||||
*/
|
*/
|
||||||
protected function sanitizeBaseName(string $name): string
|
protected function sanitizeBaseName(string $name): string
|
||||||
{
|
{
|
||||||
substr_count($name, '/') and $name = dirname($name);
|
if (substr_count($name, '/')) {
|
||||||
|
$name = dirname($name);
|
||||||
|
}
|
||||||
|
|
||||||
return strtolower(pathinfo($name, PATHINFO_FILENAME));
|
return strtolower(pathinfo($name, PATHINFO_FILENAME));
|
||||||
}
|
}
|
||||||
|
@ -162,7 +147,9 @@ class BaseProperties implements Properties
|
||||||
{
|
{
|
||||||
$value = $this->get(self::PROP_REQUIRES_WP);
|
$value = $this->get(self::PROP_REQUIRES_WP);
|
||||||
|
|
||||||
return $value && is_string($value) ? $value : null;
|
return (($value !== '') && is_string($value))
|
||||||
|
? $value
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,11 +159,13 @@ class BaseProperties implements Properties
|
||||||
{
|
{
|
||||||
$value = $this->get(self::PROP_REQUIRES_PHP);
|
$value = $this->get(self::PROP_REQUIRES_PHP);
|
||||||
|
|
||||||
return $value && is_string($value) ? $value : null;
|
return (($value !== '') && is_string($value))
|
||||||
|
? $value
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public function tags(): array
|
public function tags(): array
|
||||||
{
|
{
|
||||||
|
@ -185,7 +174,8 @@ class BaseProperties implements Properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param null $default
|
* @param mixed $default
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function get(string $key, $default = null)
|
public function get(string $key, $default = null)
|
||||||
|
@ -195,6 +185,7 @@ class BaseProperties implements Properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function has(string $key): bool
|
public function has(string $key): bool
|
||||||
|
|
|
@ -5,17 +5,26 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class LibraryProperties
|
* @phpstan-type ComposerAuthor array{
|
||||||
*
|
* name: string,
|
||||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
* email?: string,
|
||||||
|
* homepage?: string,
|
||||||
|
* role?: string,
|
||||||
|
* }
|
||||||
|
* @phpstan-type ComposerData array{
|
||||||
|
* name: string,
|
||||||
|
* version?: string,
|
||||||
|
* require?: array<string, string>,
|
||||||
|
* require-dev?: array<string, string>,
|
||||||
|
* description?: string,
|
||||||
|
* keywords?: string[],
|
||||||
|
* authors?: ComposerAuthor[],
|
||||||
|
* extra?: array{modularity?: array<string, string>},
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
class LibraryProperties extends BaseProperties
|
class LibraryProperties extends BaseProperties
|
||||||
{
|
{
|
||||||
/**
|
/** Allowed configuration in composer.json "extra.modularity" */
|
||||||
* Allowed configuration in composer.json "extra.modularity".
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public const EXTRA_KEYS = [
|
public const EXTRA_KEYS = [
|
||||||
self::PROP_DOMAIN_PATH,
|
self::PROP_DOMAIN_PATH,
|
||||||
self::PROP_NAME,
|
self::PROP_NAME,
|
||||||
|
@ -31,32 +40,33 @@ class LibraryProperties extends BaseProperties
|
||||||
*
|
*
|
||||||
* @return LibraryProperties
|
* @return LibraryProperties
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* phpcs:disable SlevomatCodingStandard.Complexity
|
||||||
* @psalm-suppress MixedArrayAccess
|
|
||||||
*/
|
*/
|
||||||
public static function new(string $composerJsonFile, ?string $baseUrl = null): LibraryProperties
|
public static function new(string $composerJsonFile, ?string $baseUrl = null): LibraryProperties
|
||||||
{
|
{
|
||||||
if (!\is_file($composerJsonFile) || !\is_readable($composerJsonFile)) {
|
// phpcs:enable SlevomatCodingStandard.Complexity
|
||||||
throw new \Exception("File {$composerJsonFile} does not exist or is not readable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = (string) file_get_contents($composerJsonFile);
|
$composerJsonData = self::readComposerJsonData($composerJsonFile);
|
||||||
/** @var array $composerJsonData */
|
|
||||||
$composerJsonData = json_decode($content, true);
|
|
||||||
|
|
||||||
$properties = Properties::DEFAULT_PROPERTIES;
|
$properties = Properties::DEFAULT_PROPERTIES;
|
||||||
$properties[self::PROP_DESCRIPTION] = $composerJsonData['description'] ?? '';
|
$properties[self::PROP_DESCRIPTION] = $composerJsonData['description'] ?? '';
|
||||||
$properties[self::PROP_TAGS] = $composerJsonData['keywords'] ?? [];
|
$properties[self::PROP_TAGS] = $composerJsonData['keywords'] ?? [];
|
||||||
|
|
||||||
$authors = $composerJsonData['authors'] ?? [];
|
$authors = $composerJsonData['authors'] ?? [];
|
||||||
|
if (!is_array($authors)) {
|
||||||
|
$authors = [];
|
||||||
|
}
|
||||||
$names = [];
|
$names = [];
|
||||||
foreach ((array) $authors as $author) {
|
foreach ($authors as $author) {
|
||||||
$name = $author['name'] ?? null;
|
if (!is_array($author)) {
|
||||||
if ($name && is_string($name)) {
|
continue;
|
||||||
|
}
|
||||||
|
$name = $author['name'] ?? '';
|
||||||
|
if (($name !== '') && is_string($name)) {
|
||||||
$names[] = $name;
|
$names[] = $name;
|
||||||
}
|
}
|
||||||
$url = $author['homepage'] ?? null;
|
$url = $author['homepage'] ?? '';
|
||||||
if ($url && !$properties['authorUri'] && is_string($url)) {
|
if (($url !== '') && ($properties[self::PROP_AUTHOR_URI] === '') && is_string($url)) {
|
||||||
$properties[self::PROP_AUTHOR_URI] = $url;
|
$properties[self::PROP_AUTHOR_URI] = $url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +76,9 @@ class LibraryProperties extends BaseProperties
|
||||||
|
|
||||||
// Custom settings which can be stored in composer.json "extra.modularity"
|
// Custom settings which can be stored in composer.json "extra.modularity"
|
||||||
$extra = $composerJsonData['extra']['modularity'] ?? [];
|
$extra = $composerJsonData['extra']['modularity'] ?? [];
|
||||||
|
if (!is_array($extra)) {
|
||||||
|
$extra = [];
|
||||||
|
}
|
||||||
foreach (self::EXTRA_KEYS as $key) {
|
foreach (self::EXTRA_KEYS as $key) {
|
||||||
$properties[$key] = $extra[$key] ?? '';
|
$properties[$key] = $extra[$key] ?? '';
|
||||||
}
|
}
|
||||||
|
@ -74,39 +87,50 @@ class LibraryProperties extends BaseProperties
|
||||||
$properties[self::PROP_REQUIRES_PHP] = self::extractPhpVersion($composerJsonData);
|
$properties[self::PROP_REQUIRES_PHP] = self::extractPhpVersion($composerJsonData);
|
||||||
|
|
||||||
// composer.json might have "version" in root
|
// composer.json might have "version" in root
|
||||||
$version = $composerJsonData['version'] ?? null;
|
$version = $composerJsonData['version'] ?? '';
|
||||||
if ($version && is_string($version)) {
|
if (($version !== '') && is_string($version)) {
|
||||||
$properties[self::PROP_VERSION] = $version;
|
$properties[self::PROP_VERSION] = $version;
|
||||||
}
|
}
|
||||||
|
|
||||||
[$baseName, $name] = static::buildNames($composerJsonData);
|
[$baseName, $name] = static::buildNames($composerJsonData);
|
||||||
$basePath = dirname($composerJsonFile);
|
$basePath = dirname($composerJsonFile);
|
||||||
if (empty($properties[self::PROP_NAME])) {
|
if (($properties[self::PROP_NAME] === '') || !is_string($properties[self::PROP_NAME])) {
|
||||||
$properties[self::PROP_NAME] = $name;
|
$properties[self::PROP_NAME] = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new self(
|
return new self($baseName, $basePath, $baseUrl, $properties);
|
||||||
$baseName,
|
|
||||||
$basePath,
|
|
||||||
$baseUrl,
|
|
||||||
$properties
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $composerJsonData
|
* @param string $url
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withBaseUrl(string $url): LibraryProperties
|
||||||
|
{
|
||||||
|
if ($this->baseUrl !== null) {
|
||||||
|
throw new \Exception(sprintf('%s::$baseUrl property is not overridable.', __CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->baseUrl = trailingslashit($url);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ComposerData $composerJsonData
|
||||||
*
|
*
|
||||||
* @return array{string, string}
|
* @return array{string, string}
|
||||||
*/
|
*/
|
||||||
private static function buildNames(array $composerJsonData): array
|
protected static function buildNames(array $composerJsonData): array
|
||||||
{
|
{
|
||||||
$composerName = (string) ($composerJsonData['name'] ?? '');
|
$composerName = (string) ($composerJsonData['name'] ?? '');
|
||||||
$packageNamePieces = explode('/', $composerName, 2);
|
$packageNamePieces = explode('/', $composerName, 2);
|
||||||
$basename = implode('-', $packageNamePieces);
|
$basename = implode('-', $packageNamePieces);
|
||||||
// "inpsyde/foo-bar-baz" => "Inpsyde Foo Bar Baz"
|
// From "syde/foo-bar-baz" to "Syde Foo Bar Baz"
|
||||||
$name = mb_convert_case(
|
$name = mb_convert_case(
|
||||||
str_replace(['-', '_', '.'], ' ', implode(' ', $packageNamePieces)),
|
str_replace(['-', '_', '.'], ' ', implode(' ', $packageNamePieces)),
|
||||||
MB_CASE_TITLE
|
MB_CASE_TITLE,
|
||||||
);
|
);
|
||||||
|
|
||||||
return [$basename, $name];
|
return [$basename, $name];
|
||||||
|
@ -122,88 +146,111 @@ class LibraryProperties extends BaseProperties
|
||||||
* `5.6 || >= 7.1` returns `5.6`
|
* `5.6 || >= 7.1` returns `5.6`
|
||||||
* `>= 7.1 < 8` returns `7.1`
|
* `>= 7.1 < 8` returns `7.1`
|
||||||
*
|
*
|
||||||
* @param array $composerData
|
* @param ComposerData $composerData
|
||||||
* @param string $key
|
* @param string $key
|
||||||
*
|
*
|
||||||
* @return string|null
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function extractPhpVersion(array $composerData, string $key = 'require'): ?string
|
protected static function extractPhpVersion(
|
||||||
{
|
array $composerData,
|
||||||
|
string $key = 'require'
|
||||||
|
): string {
|
||||||
|
|
||||||
$nextKey = ($key === 'require')
|
$nextKey = ($key === 'require')
|
||||||
? 'require-dev'
|
? 'require-dev'
|
||||||
: null;
|
: null;
|
||||||
$base = (array) ($composerData[$key] ?? []);
|
$base = $composerData[$key] ?? null;
|
||||||
$requirement = $base['php'] ?? null;
|
$requirement = is_array($base)
|
||||||
$version = ($requirement && is_string($requirement))
|
? ($base['php'] ?? '')
|
||||||
|
: '';
|
||||||
|
$version = (($requirement !== '') && is_string($requirement))
|
||||||
? trim($requirement)
|
? trim($requirement)
|
||||||
: null;
|
: '';
|
||||||
if (!$version) {
|
if ($version === '') {
|
||||||
return $nextKey
|
return ($nextKey !== null)
|
||||||
? static::extractPhpVersion($composerData, $nextKey)
|
? static::extractPhpVersion($composerData, $nextKey)
|
||||||
: null;
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
static $matcher;
|
|
||||||
$matcher or $matcher = static function (string $version): ?string {
|
|
||||||
$version = trim($version);
|
|
||||||
if (!$version) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// versions range like `>= 7.2.4 < 8`
|
|
||||||
if (preg_match('{>=?([\s0-9\.]+)<}', $version, $matches)) {
|
|
||||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// aliases like `dev-src#abcde as 7.4`
|
|
||||||
if (preg_match('{as\s*([\s0-9\.]+)}', $version, $matches)) {
|
|
||||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic requirements like 7.2, >=7.2, ^7.2, ~7.2
|
|
||||||
if (preg_match('{^(?:[>=\s~\^]+)?([0-9\.]+)}', $version, $matches)) {
|
|
||||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// support for simpler requirements like `7.3`, `>=7.4` or alternative like `5.6 || >=7`
|
// support for simpler requirements like `7.3`, `>=7.4` or alternative like `5.6 || >=7`
|
||||||
|
|
||||||
$alternatives = explode('||', $version);
|
$alternatives = explode('||', $version);
|
||||||
|
/** @var non-empty-string|null $found */
|
||||||
$found = null;
|
$found = null;
|
||||||
foreach ($alternatives as $alternative) {
|
foreach ($alternatives as $alternative) {
|
||||||
/** @var callable(string):?string $matcher */
|
$itemFound = static::parseVersion($alternative);
|
||||||
$itemFound = $matcher($alternative);
|
if (
|
||||||
if ($itemFound && (!$found || version_compare($itemFound, $found, '<'))) {
|
($itemFound !== '')
|
||||||
|
&& (($found === null) || version_compare($itemFound, $found, '<'))
|
||||||
|
) {
|
||||||
$found = $itemFound;
|
$found = $itemFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($found) {
|
if ($found !== null) {
|
||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nextKey
|
return ($nextKey !== null)
|
||||||
? static::extractPhpVersion($composerData, $nextKey)
|
? static::extractPhpVersion($composerData, $nextKey)
|
||||||
: null;
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $url
|
* @param string $version
|
||||||
*
|
*
|
||||||
* @return static
|
* @return string
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function withBaseUrl(string $url): LibraryProperties
|
protected static function parseVersion(string $version): string
|
||||||
{
|
{
|
||||||
if ($this->baseUrl !== null) {
|
$version = trim($version);
|
||||||
throw new \Exception(sprintf('%s::$baseUrl property is not overridable.', __CLASS__));
|
if ($version === '') {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->baseUrl = trailingslashit($url);
|
// versions range like `>= 7.2.4 < 8`
|
||||||
|
if (preg_match('{>=?([\s0-9\.]+)<}', $version, $matches)) {
|
||||||
|
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
// aliases like `dev-src#abcde as 7.4`
|
||||||
|
if (preg_match('{as\s*([\s0-9\.]+)}', $version, $matches)) {
|
||||||
|
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic requirements like 7.2, >=7.2, ^7.2, ~7.2
|
||||||
|
if (preg_match('{^(?:[>=\s~\^]+)?([0-9\.]+)}', $version, $matches)) {
|
||||||
|
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $composerJsonFile
|
||||||
|
*
|
||||||
|
* @return ComposerData
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private static function readComposerJsonData(string $composerJsonFile): array
|
||||||
|
{
|
||||||
|
if (!\is_file($composerJsonFile) || !\is_readable($composerJsonFile)) {
|
||||||
|
throw new \Exception(
|
||||||
|
esc_html("File {$composerJsonFile} does not exist or is not readable."),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = (string) file_get_contents($composerJsonFile);
|
||||||
|
|
||||||
|
/** @var ComposerData $composerJsonData */
|
||||||
|
$composerJsonData = json_decode($content, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
throw new \Exception(
|
||||||
|
esc_html("Error reading file {$composerJsonFile}: " . json_last_error_msg()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $composerJsonData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PluginProperties
|
|
||||||
*
|
|
||||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
|
||||||
*
|
|
||||||
* @psalm-suppress PossiblyFalseArgument, InvalidArgument
|
|
||||||
*/
|
|
||||||
class PluginProperties extends BaseProperties
|
class PluginProperties extends BaseProperties
|
||||||
{
|
{
|
||||||
/**
|
// Custom properties for Plugins
|
||||||
* Custom properties for Plugins.
|
|
||||||
*/
|
|
||||||
public const PROP_NETWORK = 'network';
|
public const PROP_NETWORK = 'network';
|
||||||
|
public const PROP_REQUIRES_PLUGINS = 'requiresPlugins';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available methods of Properties::__call()
|
* @see https://developer.wordpress.org/reference/functions/get_plugin_data/
|
||||||
* from plugin headers.
|
|
||||||
*
|
|
||||||
* @link https://developer.wordpress.org/reference/functions/get_plugin_data/
|
|
||||||
*/
|
*/
|
||||||
protected const HEADERS = [
|
protected const HEADERS = [
|
||||||
self::PROP_AUTHOR => 'Author',
|
self::PROP_AUTHOR => 'Author',
|
||||||
|
@ -37,36 +27,17 @@ class PluginProperties extends BaseProperties
|
||||||
|
|
||||||
// additional headers
|
// additional headers
|
||||||
self::PROP_NETWORK => 'Network',
|
self::PROP_NETWORK => 'Network',
|
||||||
|
self::PROP_REQUIRES_PLUGINS => 'RequiresPlugins',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
private string $pluginMainFile;
|
||||||
* @var string
|
private string $pluginBaseName;
|
||||||
*/
|
protected ?bool $isMu = null;
|
||||||
private $pluginMainFile;
|
protected ?bool $isActive = null;
|
||||||
|
protected ?bool $isNetworkActive = null;
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $pluginBaseName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
protected $isMu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
protected $isActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
protected $isNetworkActive;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $pluginMainFile
|
* @param string $pluginMainFile
|
||||||
*
|
|
||||||
* @return PluginProperties
|
* @return PluginProperties
|
||||||
*/
|
*/
|
||||||
public static function new(string $pluginMainFile): PluginProperties
|
public static function new(string $pluginMainFile): PluginProperties
|
||||||
|
@ -75,8 +46,6 @@ class PluginProperties extends BaseProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PluginProperties constructor.
|
|
||||||
*
|
|
||||||
* @param string $pluginMainFile
|
* @param string $pluginMainFile
|
||||||
*/
|
*/
|
||||||
protected function __construct(string $pluginMainFile)
|
protected function __construct(string $pluginMainFile)
|
||||||
|
@ -85,7 +54,11 @@ class PluginProperties extends BaseProperties
|
||||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
$pluginData = get_plugin_data($pluginMainFile);
|
// $markup = false, to avoid an incorrect early wptexturize call.
|
||||||
|
// $translate = false, to avoid loading translations too early
|
||||||
|
// @see https://core.trac.wordpress.org/ticket/49965
|
||||||
|
// @see https://core.trac.wordpress.org/ticket/34114
|
||||||
|
$pluginData = (array) get_plugin_data($pluginMainFile, false, false);
|
||||||
$properties = Properties::DEFAULT_PROPERTIES;
|
$properties = Properties::DEFAULT_PROPERTIES;
|
||||||
|
|
||||||
// Map pluginData to internal structure.
|
// Map pluginData to internal structure.
|
||||||
|
@ -93,6 +66,7 @@ class PluginProperties extends BaseProperties
|
||||||
$properties[$key] = $pluginData[$pluginDataKey] ?? '';
|
$properties[$key] = $pluginData[$pluginDataKey] ?? '';
|
||||||
unset($pluginData[$pluginDataKey]);
|
unset($pluginData[$pluginDataKey]);
|
||||||
}
|
}
|
||||||
|
/** @var array<string, mixed> $properties */
|
||||||
$properties = array_merge($properties, $pluginData);
|
$properties = array_merge($properties, $pluginData);
|
||||||
|
|
||||||
$this->pluginMainFile = wp_normalize_path($pluginMainFile);
|
$this->pluginMainFile = wp_normalize_path($pluginMainFile);
|
||||||
|
@ -119,14 +93,22 @@ class PluginProperties extends BaseProperties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
|
||||||
* @psalm-suppress PossiblyFalseArgument
|
|
||||||
*/
|
*/
|
||||||
public function network(): bool
|
public function network(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->get(self::PROP_NETWORK, false);
|
return (bool) $this->get(self::PROP_NETWORK, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function requiresPlugins(): array
|
||||||
|
{
|
||||||
|
$value = $this->get(self::PROP_REQUIRES_PLUGINS);
|
||||||
|
|
||||||
|
return $value && is_string($value) ? explode(',', $value) : [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -163,10 +145,6 @@ class PluginProperties extends BaseProperties
|
||||||
public function isMuPlugin(): bool
|
public function isMuPlugin(): bool
|
||||||
{
|
{
|
||||||
if ($this->isMu === null) {
|
if ($this->isMu === null) {
|
||||||
/**
|
|
||||||
* @psalm-suppress UndefinedConstant
|
|
||||||
* @psalm-suppress MixedArgument
|
|
||||||
*/
|
|
||||||
$muPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR);
|
$muPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR);
|
||||||
$this->isMu = strpos($this->pluginMainFile, $muPluginDir) === 0;
|
$this->isMu = strpos($this->pluginMainFile, $muPluginDir) === 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,7 @@ interface Properties
|
||||||
public const PROP_REQUIRES_WP = 'requiresWp';
|
public const PROP_REQUIRES_WP = 'requiresWp';
|
||||||
public const PROP_REQUIRES_PHP = 'requiresPhp';
|
public const PROP_REQUIRES_PHP = 'requiresPhp';
|
||||||
public const PROP_TAGS = 'tags';
|
public const PROP_TAGS = 'tags';
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public const DEFAULT_PROPERTIES = [
|
public const DEFAULT_PROPERTIES = [
|
||||||
self::PROP_AUTHOR => '',
|
self::PROP_AUTHOR => '',
|
||||||
self::PROP_AUTHOR_URI => '',
|
self::PROP_AUTHOR_URI => '',
|
||||||
|
@ -36,15 +34,13 @@ interface Properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param null $default
|
* @param mixed $default
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function get(string $key, $default = null);
|
public function get(string $key, $default = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function has(string $key): bool;
|
public function has(string $key): bool;
|
||||||
|
@ -103,6 +99,7 @@ interface Properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The home page of the plugin, theme or library.
|
* The home page of the plugin, theme or library.
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function uri(): string;
|
public function uri(): string;
|
||||||
|
@ -122,7 +119,7 @@ interface Properties
|
||||||
/**
|
/**
|
||||||
* Optional. Specify the minimum required PHP version.
|
* Optional. Specify the minimum required PHP version.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function requiresPhp(): ?string;
|
public function requiresPhp(): ?string;
|
||||||
|
|
||||||
|
@ -130,10 +127,10 @@ interface Properties
|
||||||
* Optional. Currently, only available for Theme and Library.
|
* Optional. Currently, only available for Theme and Library.
|
||||||
* Plugins do not have support for "tags"/"keywords" in header.
|
* Plugins do not have support for "tags"/"keywords" in header.
|
||||||
*
|
*
|
||||||
* @link https://developer.wordpress.org/reference/classes/wp_theme/#properties
|
* @return string[]
|
||||||
* @link https://getcomposer.org/doc/04-schema.md#keywords
|
|
||||||
*
|
*
|
||||||
* @return array
|
* @see https://developer.wordpress.org/reference/classes/wp_theme/#properties
|
||||||
|
* @see https://getcomposer.org/doc/04-schema.md#keywords
|
||||||
*/
|
*/
|
||||||
public function tags(): array;
|
public function tags(): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,12 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ThemeProperties
|
|
||||||
*
|
|
||||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
|
||||||
*
|
|
||||||
* @psalm-suppress PossiblyFalseArgument, InvalidArgument
|
|
||||||
*/
|
|
||||||
class ThemeProperties extends BaseProperties
|
class ThemeProperties extends BaseProperties
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Additional properties specific for themes.
|
|
||||||
*/
|
|
||||||
public const PROP_STATUS = 'status';
|
public const PROP_STATUS = 'status';
|
||||||
public const PROP_TEMPLATE = 'template';
|
public const PROP_TEMPLATE = 'template';
|
||||||
/**
|
|
||||||
* Available methods of Properties::__call()
|
/** @see https://developer.wordpress.org/reference/classes/wp_theme/ */
|
||||||
* from theme headers.
|
|
||||||
*
|
|
||||||
* @link https://developer.wordpress.org/reference/classes/wp_theme/
|
|
||||||
*/
|
|
||||||
protected const HEADERS = [
|
protected const HEADERS = [
|
||||||
self::PROP_AUTHOR => 'Author',
|
self::PROP_AUTHOR => 'Author',
|
||||||
self::PROP_AUTHOR_URI => 'AuthorURI',
|
self::PROP_AUTHOR_URI => 'AuthorURI',
|
||||||
|
@ -53,8 +39,6 @@ class ThemeProperties extends BaseProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ThemeProperties constructor.
|
|
||||||
*
|
|
||||||
* @param string $themeDirectory
|
* @param string $themeDirectory
|
||||||
*/
|
*/
|
||||||
protected function __construct(string $themeDirectory)
|
protected function __construct(string $themeDirectory)
|
||||||
|
@ -67,13 +51,15 @@ class ThemeProperties extends BaseProperties
|
||||||
$properties = Properties::DEFAULT_PROPERTIES;
|
$properties = Properties::DEFAULT_PROPERTIES;
|
||||||
|
|
||||||
foreach (self::HEADERS as $key => $themeKey) {
|
foreach (self::HEADERS as $key => $themeKey) {
|
||||||
/** @psalm-suppress DocblockTypeContradiction */
|
$property = $theme->get($themeKey);
|
||||||
$properties[$key] = $theme->get($themeKey) ?? '';
|
if (is_string($property) || is_array($property)) {
|
||||||
|
$properties[$key] = $property;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$baseName = $theme->get_stylesheet();
|
$baseName = $theme->get_stylesheet();
|
||||||
$basePath = $theme->get_stylesheet_directory();
|
$basePath = $theme->get_stylesheet_directory();
|
||||||
$baseUrl = (string) trailingslashit($theme->get_stylesheet_directory_uri());
|
$baseUrl = trailingslashit($theme->get_stylesheet_directory_uri());
|
||||||
|
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
$baseName,
|
$baseName,
|
||||||
|
@ -84,8 +70,6 @@ class ThemeProperties extends BaseProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the theme is published.
|
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function status(): string
|
public function status(): string
|
||||||
|
@ -93,6 +77,9 @@ class ThemeProperties extends BaseProperties
|
||||||
return (string) $this->get(self::PROP_STATUS);
|
return (string) $this->get(self::PROP_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function template(): string
|
public function template(): string
|
||||||
{
|
{
|
||||||
return (string) $this->get(self::PROP_TEMPLATE);
|
return (string) $this->get(self::PROP_TEMPLATE);
|
||||||
|
@ -120,7 +107,7 @@ class ThemeProperties extends BaseProperties
|
||||||
public function parentThemeProperties(): ?ThemeProperties
|
public function parentThemeProperties(): ?ThemeProperties
|
||||||
{
|
{
|
||||||
$template = $this->template();
|
$template = $this->template();
|
||||||
if (!$template) {
|
if ($template === '') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Vendor\Psr\Container;
|
namespace WooCommerce\PayPalCommerce\Vendor\Psr\Container;
|
||||||
|
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base interface representing a generic exception in a container.
|
* Base interface representing a generic exception in a container.
|
||||||
*/
|
*/
|
||||||
interface ContainerExceptionInterface
|
interface ContainerExceptionInterface extends Throwable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce;
|
namespace WooCommerce\PayPalCommerce;
|
||||||
|
|
||||||
use Dhii\Versions\StringVersionFactory;
|
use Dhii\Versions\StringVersionFactory;
|
||||||
use Inpsyde\Modularity\Properties\Properties;
|
|
||||||
use WooCommerce\PayPalCommerce\Http\RedirectorInterface;
|
use WooCommerce\PayPalCommerce\Http\RedirectorInterface;
|
||||||
use WooCommerce\PayPalCommerce\Http\WpRedirector;
|
use WooCommerce\PayPalCommerce\Http\WpRedirector;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Package;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Package;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties\Properties;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
use WpOop\WordPress\Plugin\PluginInterface;
|
use WpOop\WordPress\Plugin\PluginInterface;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue