diff --git a/.gitignore b/.gitignore index e9b28ae1901f15..1f5680e2c6c2da 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ coverage yarn.lock /artifacts /perf-envs +/composer.lock .cache *.tsbuildinfo diff --git a/composer.json b/composer.json index 4d0db1947690e3..b91bb973ade417 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,6 @@ "phpcompatibility/php-compatibility": "^9.3", "wp-coding-standards/wpcs": "^2.2", "sirbrillig/phpcs-variable-analysis": "^2.8", - "phpunit/phpunit": "^6.5", "spatie/phpunit-watcher": "^1.23", "yoast/phpunit-polyfills": "^1.0" }, diff --git a/composer.lock b/composer.lock deleted file mode 100644 index f358bb3462cab8..00000000000000 --- a/composer.lock +++ /dev/null @@ -1,3839 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "35cc4f57b35ccba38e5ac0c76c85309e", - "packages": [ - { - "name": "composer/installers", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/composer/installers.git", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "replace": { - "roundcube/plugin-installer": "*", - "shama/baton": "*" - }, - "require-dev": { - "composer/composer": "1.6.* || ^2.0", - "composer/semver": "^1 || ^3", - "phpstan/phpstan": "^0.12.55", - "phpstan/phpstan-phpunit": "^0.12.16", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.3" - }, - "type": "composer-plugin", - "extra": { - "class": "Composer\\Installers\\Plugin", - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Installers\\": "src/Composer/Installers" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kyle Robinson Young", - "email": "kyle@dontkry.com", - "homepage": "https://github.com/shama" - } - ], - "description": "A multi-framework Composer library installer", - "homepage": "https://composer.github.io/installers/", - "keywords": [ - "Craft", - "Dolibarr", - "Eliasis", - "Hurad", - "ImageCMS", - "Kanboard", - "Lan Management System", - "MODX Evo", - "MantisBT", - "Mautic", - "Maya", - "OXID", - "Plentymarkets", - "Porto", - "RadPHP", - "SMF", - "Starbug", - "Thelia", - "Whmcs", - "WolfCMS", - "agl", - "aimeos", - "annotatecms", - "attogram", - "bitrix", - "cakephp", - "chef", - "cockpit", - "codeigniter", - "concrete5", - "croogo", - "dokuwiki", - "drupal", - "eZ Platform", - "elgg", - "expressionengine", - "fuelphp", - "grav", - "installer", - "itop", - "joomla", - "known", - "kohana", - "laravel", - "lavalite", - "lithium", - "magento", - "majima", - "mako", - "mediawiki", - "miaoxing", - "modulework", - "modx", - "moodle", - "osclass", - "pantheon", - "phpbb", - "piwik", - "ppi", - "processwire", - "puppet", - "pxcms", - "reindex", - "roundcube", - "shopware", - "silverstripe", - "sydes", - "sylius", - "symfony", - "tastyigniter", - "typo3", - "wordpress", - "yawik", - "zend", - "zikula" - ], - "support": { - "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.12.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-09-13T08:19:44+00:00" - } - ], - "packages-dev": [ - { - "name": "clue/stdio-react", - "version": "v2.6.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-stdio.git", - "reference": "dfa6c378aabdff718202d4e2453f752c38ea3399" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-stdio/zipball/dfa6c378aabdff718202d4e2453f752c38ea3399", - "reference": "dfa6c378aabdff718202d4e2453f752c38ea3399", - "shasum": "" - }, - "require": { - "clue/term-react": "^1.0 || ^0.1.1", - "clue/utf8-react": "^1.0 || ^0.1", - "php": ">=5.3", - "react/event-loop": "^1.2", - "react/stream": "^1.2" - }, - "require-dev": { - "clue/arguments": "^2.0", - "clue/commander": "^1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-mbstring": "Using ext-mbstring should provide slightly better performance for handling I/O" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Stdio\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Async, event-driven console input & output (STDIN, STDOUT) for truly interactive CLI applications, built on top of ReactPHP", - "homepage": "https://github.com/clue/reactphp-stdio", - "keywords": [ - "async", - "autocomplete", - "autocompletion", - "cli", - "history", - "interactive", - "reactphp", - "readline", - "stdin", - "stdio", - "stdout" - ], - "support": { - "issues": "https://github.com/clue/reactphp-stdio/issues", - "source": "https://github.com/clue/reactphp-stdio/tree/v2.6.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-03-18T15:09:30+00:00" - }, - { - "name": "clue/term-react", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-term.git", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-term/zipball/eb6eb063eda04a714ef89f066586a2c49588f7ca", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.0 || ^0.7" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Term\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming terminal emulator, built on top of ReactPHP.", - "homepage": "https://github.com/clue/reactphp-term", - "keywords": [ - "C0", - "CSI", - "ansi", - "apc", - "ascii", - "c1", - "control codes", - "dps", - "osc", - "pm", - "reactphp", - "streaming", - "terminal", - "vt100", - "xterm" - ], - "support": { - "issues": "https://github.com/clue/reactphp-term/issues", - "source": "https://github.com/clue/reactphp-term/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-11-06T11:50:12+00:00" - }, - { - "name": "clue/utf8-react", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-utf8.git", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-utf8/zipball/8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 ||^5.7 || ^4.8", - "react/stream": "^1.0 || ^0.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Utf8\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming UTF-8 parser, built on top of ReactPHP.", - "homepage": "https://github.com/clue/reactphp-utf8", - "keywords": [ - "reactphp", - "streaming", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "issues": "https://github.com/clue/reactphp-utf8/issues", - "source": "https://github.com/clue/reactphp-utf8/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-11-06T11:48:09+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", - "source": { - "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" - }, - { - "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, - "time": "2022-02-04T12:51:07+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-03-03T08:28:38+00:00" - }, - { - "name": "evenement/evenement", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Evenement": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Événement is a very simple event dispatching library for PHP", - "keywords": [ - "event-dispatcher", - "event-emitter" - ], - "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/master" - }, - "time": "2017-07-23T21:35:13+00:00" - }, - { - "name": "jolicode/jolinotif", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/jolicode/JoliNotif.git", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/a15bfc0d5aef432f150385924ede4e099643edb7", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "symfony/process": "^4.0|^5.0|^6.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "symfony/finder": "^5.0", - "symfony/phpunit-bridge": "^5.0" - }, - "bin": [ - "jolinotif" - ], - "type": "library", - "autoload": { - "psr-4": { - "Joli\\JoliNotif\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Loïck Piera", - "email": "pyrech@gmail.com" - } - ], - "description": "Send desktop notifications on Windows, Linux, MacOS.", - "keywords": [ - "MAC", - "growl", - "linux", - "notification", - "windows" - ], - "support": { - "issues": "https://github.com/jolicode/JoliNotif/issues", - "source": "https://github.com/jolicode/JoliNotif/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/jolicode/jolinotif", - "type": "tidelift" - } - ], - "time": "2021-12-01T16:20:42+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2022-03-03T13:19:32+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" - }, - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/master" - }, - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "phpcompatibility/php-compatibility", - "version": "9.3.5", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" - } - ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, - "time": "2019-12-27T09:44:58+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/5.3" - }, - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/master" - }, - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" - }, - "abandoned": true, - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.14", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/6.5.14" - }, - "time": "2019-02-01T05:22:47+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/5.0.10" - }, - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "react/event-loop", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/187fb56f46d424afb6ec4ad089269c72eec2e137", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], - "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-03-17T11:10:22+00:00" - }, - { - "name": "react/stream", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" - }, - "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], - "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T12:37:55+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/master" - }, - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/master" - }, - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/master" - }, - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-11-11T13:51:24+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" - }, - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:37:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:34:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" - }, - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.3", - "source": { - "type": "git", - "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "c921498b474212fe4552928bbeb68d70250ce5e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/c921498b474212fe4552928bbeb68d70250ce5e8", - "reference": "c921498b474212fe4552928bbeb68d70250ce5e8", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "limedeck/phpunit-detailed-printer": "^3.1 || ^4.0 || ^5.0", - "phpstan/phpstan": "^0.11.8", - "phpunit/phpunit": "^5.0 || ^6.5 || ^7.0 || ^8.0", - "sirbrillig/phpcs-import-detection": "^1.1" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "VariableAnalysis\\": "VariableAnalysis/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Sam Graham", - "email": "php-codesniffer-variableanalysis@illusori.co.uk" - }, - { - "name": "Payton Swick", - "email": "payton@foolord.com" - } - ], - "description": "A PHPCS sniff to detect problems with variables.", - "support": { - "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", - "source": "https://github.com/sirbrillig/phpcs-variable-analysis", - "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" - }, - "time": "2022-02-21T17:01:13+00:00" - }, - { - "name": "spatie/phpunit-watcher", - "version": "1.23.6", - "source": { - "type": "git", - "url": "https://github.com/spatie/phpunit-watcher.git", - "reference": "c192fff763810c8378511bcf0069df4b91478866" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/phpunit-watcher/zipball/c192fff763810c8378511bcf0069df4b91478866", - "reference": "c192fff763810c8378511bcf0069df4b91478866", - "shasum": "" - }, - "require": { - "clue/stdio-react": "^2.4", - "jolicode/jolinotif": "^2.2", - "php": "^7.2 | ^8.0 | ^8.1", - "symfony/console": "^5 | ^6", - "symfony/finder": "^5.4 | ^6", - "symfony/process": "^5.4 | ^6", - "symfony/yaml": "^5.2 | ^6", - "yosymfony/resource-watcher": "^2.0 | ^3.0" - }, - "conflict": { - "symfony/console": "<5.2", - "yosymfony/resource-watcher": "<2.0" - }, - "require-dev": { - "phpunit/phpunit": "^8.6 | ^9.0" - }, - "bin": [ - "phpunit-watcher" - ], - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\PhpUnitWatcher\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Automatically rerun PHPUnit tests when source code changes", - "homepage": "https://github.com/spatie/phpunit-watcher", - "keywords": [ - "phpunit-watcher", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/phpunit-watcher/issues", - "source": "https://github.com/spatie/phpunit-watcher/tree/1.23.6" - }, - "time": "2022-01-31T11:57:13+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2022-06-18T07:21:10+00:00" - }, - { - "name": "symfony/console", - "version": "v5.4.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "4d671ab4ddac94ee439ea73649c69d9d200b5000" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4d671ab4ddac94ee439ea73649c69d9d200b5000", - "reference": "4d671ab4ddac94ee439ea73649c69d9d200b5000", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-26T13:00:04+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9b630f3427f3ebe7cd346c277a1408b00249dad9", - "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9", - "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.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-04-15T08:07:45+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-10T07:21:04+00:00" - }, - { - "name": "symfony/process", - "version": "v5.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", - "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v5.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-04-08T05:07:18+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-30T19:17:29+00:00" - }, - { - "name": "symfony/string", - "version": "v5.4.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "4432bc7df82a554b3e413a8570ce2fea90e94097" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/4432bc7df82a554b3e413a8570ce2fea90e94097", - "reference": "4432bc7df82a554b3e413a8570ce2fea90e94097", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "conflict": { - "symfony/translation-contracts": ">=3.0" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.4.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-26T15:57:47+00:00" - }, - { - "name": "symfony/yaml", - "version": "v5.4.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "04e42926429d9e8b39c174387ab990bf7817f7a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/04e42926429d9e8b39c174387ab990bf7817f7a2", - "reference": "04e42926429d9e8b39c174387ab990bf7817f7a2", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<5.3" - }, - "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-20T11:50:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "wp-coding-standards/wpcs", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "7da1894633f168fe244afc6de00d141f27517b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", - "reference": "7da1894633f168fe244afc6de00d141f27517b62", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.3.1" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", - "phpcompatibility/php-compatibility": "^9.0", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Contributors", - "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", - "keywords": [ - "phpcs", - "standards", - "wordpress" - ], - "support": { - "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", - "source": "https://github.com/WordPress/WordPress-Coding-Standards", - "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" - }, - "time": "2020-05-13T23:57:56+00:00" - }, - { - "name": "yoast/phpunit-polyfills", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "5ea3536428944955f969bc764bbe09738e151ada" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/5ea3536428944955f969bc764bbe09738e151ada", - "reference": "5ea3536428944955f969bc764bbe09738e151ada", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "require-dev": { - "yoast/yoastcs": "^2.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "autoload": { - "files": [ - "phpunitpolyfills-autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Team Yoast", - "email": "support@yoast.com", - "homepage": "https://yoast.com" - }, - { - "name": "Contributors", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" - } - ], - "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", - "keywords": [ - "phpunit", - "polyfill", - "testing" - ], - "support": { - "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", - "source": "https://github.com/Yoast/PHPUnit-Polyfills" - }, - "time": "2021-11-23T01:37:03+00:00" - }, - { - "name": "yosymfony/resource-watcher", - "version": "v3.0.0", - "source": { - "type": "git", - "url": "https://github.com/yosymfony/resource-watcher.git", - "reference": "2f197cee0231c06db865d4ad2d8d7cd3faead2f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yosymfony/resource-watcher/zipball/2f197cee0231c06db865d4ad2d8d7cd3faead2f8", - "reference": "2f197cee0231c06db865d4ad2d8d7cd3faead2f8", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "symfony/finder": "^2.7|^3.0|^4.0|^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7", - "symfony/filesystem": "^2.7|^3.0|^4.0|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Yosymfony\\ResourceWatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Victor Puertas", - "email": "vpgugr@gmail.com" - } - ], - "description": "A simple resource watcher using Symfony Finder", - "homepage": "http://yosymfony.com", - "keywords": [ - "finder", - "resources", - "symfony", - "watcher" - ], - "support": { - "issues": "https://github.com/yosymfony/resource-watcher/issues", - "source": "https://github.com/yosymfony/resource-watcher/tree/master" - }, - "time": "2020-06-10T14:58:36+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "platform-overrides": { - "php": "7.4" - }, - "plugin-api-version": "2.3.0" -} diff --git a/lib/experimental/class-wp-webfonts-provider-local.php b/lib/experimental/class-wp-webfonts-provider-local.php index f96b18ea698637..dea47dc5ddefe3 100644 --- a/lib/experimental/class-wp-webfonts-provider-local.php +++ b/lib/experimental/class-wp-webfonts-provider-local.php @@ -22,19 +22,46 @@ * generate styles from locally-hosted font files is contained * in this provider. * - * @since 6.0.0 + * @since 6.1.0 */ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider { /** * The provider's unique ID. * - * @since 6.0.0 + * @since 6.1.0 * * @var string */ protected $id = 'local'; + /** + * Holds a string which contains the type attribute for style tag. + * + * If the active theme does not declare HTML5 support for 'style', + * then it initializes as `type='text/css'`. + * + * @since 6.1.0 + * + * @var string + */ + private $type_attr = ''; + + /** + * Constructor. + * + * @since 6.1.0 + */ + public function __construct() { + if ( + function_exists( 'is_admin' ) && ! is_admin() + && + function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' ) + ) { + $this->type_attr = " type='text/css'"; + } + } + /** * Gets the `@font-face` CSS styles for locally-hosted font files. * @@ -81,7 +108,7 @@ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider { * } * * - * @since 6.0.0 + * @since 6.1.0 * * @return string The `@font-face` CSS. */ @@ -96,6 +123,12 @@ public function get_css() { $css .= '@font-face{' . $this->build_font_face_css( $webfont ) . '}'; } + $css = sprintf( + "\n", + $this->type_attr, + $css + ); + return $css; } diff --git a/lib/experimental/class-wp-webfonts-provider.php b/lib/experimental/class-wp-webfonts-provider.php index a49b8a324b7f5f..24dd93b9615048 100644 --- a/lib/experimental/class-wp-webfonts-provider.php +++ b/lib/experimental/class-wp-webfonts-provider.php @@ -69,4 +69,13 @@ public function set_webfonts( array $webfonts ) { * @return string The `@font-face` CSS. */ abstract public function get_css(); + + /** + * Prints the generated styles. + * + * @since 6.1.0 + */ + public function print_styles() { + echo $this->get_css(); + } } diff --git a/lib/experimental/class-wp-webfonts-utils.php b/lib/experimental/class-wp-webfonts-utils.php new file mode 100644 index 00000000000000..cf506209bc8a0d --- /dev/null +++ b/lib/experimental/class-wp-webfonts-utils.php @@ -0,0 +1,113 @@ +register_provider( 'local', 'WP_Webfonts_Provider_Local' ); - - // Register callback to generate and enqueue styles. - if ( did_action( 'wp_enqueue_scripts' ) ) { - $this->stylesheet_handle = 'webfonts-footer'; - $hook = 'wp_print_footer_scripts'; - } else { - $this->stylesheet_handle = 'webfonts'; - $hook = 'wp_enqueue_scripts'; - } - add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); - - // Enqueue webfonts in the block editor. - add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); + public function __construct() { + /** + * Fires when the WP_Webfonts instance is initialized. + * + * @since X.X.X + * + * @param WP_Webfonts $wp_webfonts WP_Webfonts instance (passed by reference). + */ + do_action_ref_array( 'wp_default_webfonts', array( &$this ) ); } /** - * Get the list of registered fonts. + * Gets the list of all registered web fonts and variations. * - * @since 6.0.0 + * @since 6.1.0 * - * @return array[] + * @return string[] */ - public function get_registered_webfonts() { - return $this->registered_webfonts; + public function get_registered() { + return array_keys( $this->registered ); } /** - * Get the list of enqueued fonts. + * Gets the list of enqueued web fonts and variations. + * + * @since 6.1.0 * * @return array[] */ - public function get_enqueued_webfonts() { - return $this->enqueued_webfonts; + public function get_enqueued() { + return $this->queue; } /** - * Get the list of all fonts. + * Gets a list of all registered variations for the given font family. * - * @return array[] + * @since 6.1.0 + * + * @param string $font_family_handle The font family for the variations. + * @return array An array of registered variations. */ - public function get_all_webfonts() { - return array_merge( $this->get_registered_webfonts(), $this->get_enqueued_webfonts() ); + public function get_variations( $font_family_handle ) { + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return array(); + } + + return $this->registered[ $font_family_handle ]->deps; } /** - * Get the list of providers. + * Removes a font family and all registered variations. * - * @since 6.0.0 + * @since 6.1.0 * - * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID. + * @param string $font_family_handle The font family to remove. */ - public function get_providers() { - return $this->providers; + public function remove_font_family( $font_family_handle ) { + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return; + } + + $variations = $this->registered[ $font_family_handle ]->deps; + + foreach ( $variations as $variation ) { + $this->remove( $variation ); + } + + $this->remove( $font_family_handle ); } /** - * Register a webfont. + * Registers a variation to the given font family. * - * @since 6.0.0 + * @since 6.1.0 * - * @param array $webfont Webfont to be registered. - * @return string|false The font family slug if successfully registered, else false. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. When none is provided, the + * handle will be dynamically generated. + * Default empty string. + * @return string|null Variation handle on success. Else null. */ - public function register_webfont( array $webfont ) { - $webfont = $this->validate_webfont( $webfont ); + public function add_variation( $font_family_handle, array $variation, $variation_handle = '' ) { + if ( ! WP_Webfonts_Utils::is_defined( $font_family_handle ) ) { + trigger_error( 'Font family handle must be a non-empty string.' ); + return null; + } - // If not valid, bail out. - if ( ! $webfont ) { - return false; + if ( '' !== $variation_handle && ! WP_Webfonts_Utils::is_defined( $variation_handle ) ) { + trigger_error( 'Variant handle must be a non-empty string.' ); + return null; + } + + // Register the font family when it does not yet exist. + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + if ( ! $this->add( $font_family_handle, false ) ) { + return null; + } } - $slug = $this->get_font_slug( $webfont ); + $variation = $this->validate_variation( $font_family_handle, $variation ); - // Initialize a new font-family collection. - if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { - $this->registered_webfonts[ $slug ] = array(); + // Variation validation failed. + if ( ! $variation ) { + return null; } - $this->registered_webfonts[ $slug ][] = $webfont; - return $slug; - } + if ( '' === $variation_handle ) { + $variation_handle = WP_Webfonts_Utils::convert_variation_into_handle( $font_family_handle, $variation ); + if ( is_null( $variation_handle ) ) { + return null; + } + } - /** - * Enqueue a font-family that has been already registered. - * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. - */ - public function enqueue_webfont( $font_family_name ) { - $slug = $this->get_font_slug( $font_family_name ); + // Bail out if the variant is already registered. + if ( $this->is_variation_registered( $font_family_handle, $variation_handle ) ) { + return $variation_handle; + } + + if ( array_key_exists( 'src', $variation ) ) { + $result = $this->add( $variation_handle, $variation['src'] ); + } else { + $result = $this->add( $variation_handle, false ); + } - if ( isset( $this->enqueued_webfonts[ $slug ] ) ) { - return true; + // Bail out if the registration failed. + if ( ! $result ) { + return null; } - if ( ! isset( $this->registered_webfonts[ $slug ] ) ) { - /* translators: %s unique slug to identify the font family of the webfont */ - _doing_it_wrong( __METHOD__, sprintf( __( 'The "%s" font family is not registered.', 'gutenberg' ), $slug ), '6.0.0' ); + $this->add_data( $variation_handle, 'font-properties', $variation ); - return false; + // Add the font variation as a dependency to the registered font family. + $this->add_dependency( $font_family_handle, $variation_handle ); + + $this->providers[ $variation['provider'] ]['fonts'][] = $variation_handle; + + return $variation_handle; + } + + /** + * Checks if the variation is registered. + * + * @since 6.1.0 + * + * @param string $font_family_handle The font family's handle for this variation. + * @param string $variant_handle Variation's handle. + * @return bool + */ + private function is_variation_registered( $font_family_handle, $variant_handle ) { + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return array(); } - $this->enqueued_webfonts[ $slug ] = $this->registered_webfonts[ $slug ]; - unset( $this->registered_webfonts[ $slug ] ); - return true; + return in_array( $variant_handle, $this->registered[ $font_family_handle ]->deps ); } /** - * Get the font slug. + * Removes a font variation. * - * @since 6.0.0 + * @since 6.1.0 * - * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array - * or a font-family as a string. - * @return string|false The font slug on success, or false if the font-family cannot be determined. + * @param string $font_family_handle The font family for this variation. + * @param string $variation_handle The variation's handle to remove. */ - public static function get_font_slug( $to_convert ) { - if ( is_array( $to_convert ) ) { - if ( isset( $to_convert['font-family'] ) ) { - $to_convert = $to_convert['font-family']; - } elseif ( isset( $to_convert['fontFamily'] ) ) { - $to_convert = $to_convert['fontFamily']; - } else { - _doing_it_wrong( __METHOD__, __( 'Could not determine the font family name.', 'gutenberg' ), '6.0.0' ); - return false; - } + public function remove_variation( $font_family_handle, $variation_handle ) { + $this->remove( $font_family_handle ); + + if ( ! isset( $this->registered[ $font_family_handle ] ) ) { + return; } - return sanitize_title( $to_convert ); + // Remove the variation as a dependency. + $this->registered[ $font_family_handle ]->deps = array_values( + array_diff( + $this->registered[ $font_family_handle ]->deps, + array( $variation_handle ) + ) + ); } /** - * Validate a webfont. + * Adds a variation as a dependency to the given font family. * - * @since 6.0.0 + * @since 6.1.0 + * + * @param string $font_family_handle The font family's handle for this variation. + * @param string $variation_handle The variation's handle. + */ + private function add_dependency( $font_family_handle, $variation_handle ) { + $this->registered[ $font_family_handle ]->deps[] = $variation_handle; + } + + /** + * Validates and sanitizes a variation. * - * @param array $webfont The webfont arguments. + * @since 6.1.0 * - * @return array|false The validated webfont arguments, or false if the webfont is invalid. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @return array|false|object */ - public function validate_webfont( $webfont ) { - $webfont = wp_parse_args( - $webfont, - array( - 'provider' => 'local', - 'font-family' => '', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-display' => 'fallback', - ) + private function validate_variation( $font_family_handle, $variation ) { + $defaults = array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', ); - // Check the font-family. - if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) { - trigger_error( __( 'Webfont font family must be a non-empty string.', 'gutenberg' ) ); - return false; - } + $defaults = apply_filters( 'wp_webfont_variation_defaults', $defaults ); + + $defaults['font-family'] = $font_family_handle; + $variation = wp_parse_args( $variation, $defaults ); // Local fonts need a "src". - if ( 'local' === $webfont['provider'] ) { + if ( 'local' === $variation['provider'] ) { // Make sure that local fonts have 'src' defined. - if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) { + if ( empty( $variation['src'] ) || ( ! is_string( $variation['src'] ) && ! is_array( $variation['src'] ) ) ) { trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.', 'gutenberg' ) ); return false; } + } elseif ( ! class_exists( $variation['provider'] ) ) { + trigger_error( __( 'The provider class specified does not exist.', 'gutenberg' ) ); + return false; } // Validate the 'src' property. - if ( ! empty( $webfont['src'] ) ) { - foreach ( (array) $webfont['src'] as $src ) { + if ( ! empty( $variation['src'] ) ) { + foreach ( (array) $variation['src'] as $src ) { if ( empty( $src ) || ! is_string( $src ) ) { trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) ); return false; @@ -238,14 +282,14 @@ public function validate_webfont( $webfont ) { } // Check the font-weight. - if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) { - trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.', 'gutenberg' ) ); + if ( ! is_string( $variation['font-weight'] ) && ! is_int( $variation['font-weight'] ) ) { + trigger_error( __( 'Webfont font-weight must be a properly formatted string or integer.', 'gutenberg' ) ); return false; } // Check the font-display. - if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { - $webfont['font-display'] = 'fallback'; + if ( ! in_array( $variation['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) { + $variation['font-display'] = 'fallback'; } $valid_props = array( @@ -268,150 +312,172 @@ public function validate_webfont( $webfont ) { 'provider', ); - foreach ( $webfont as $prop => $value ) { + foreach ( $variation as $prop => $value ) { if ( ! in_array( $prop, $valid_props, true ) ) { - unset( $webfont[ $prop ] ); + unset( $variation[ $prop ] ); } } - return $webfont; + return $variation; } /** - * Register a provider. + * Processes the items and dependencies. * - * @since 6.0.0 + * Processes the items passed to it or the queue, and their dependencies. * - * @param string $provider The provider name. - * @param string $class The provider class name. - * @return bool True if successfully registered, else false. + * @since 6.1.0 + * + * @param string|string[]|false $handles Optional. Items to be processed: queue (false), + * single item (string), or multiple items (array of strings). + * Default false. + * @param int|false $group Optional. Group level: level (int), no group (false). + * + * @return array|string[] Array of web font handles that have been processed. + * An empty array if none were processed. */ - public function register_provider( $provider, $class ) { - if ( empty( $provider ) || empty( $class ) ) { - return false; + public function do_items( $handles = false, $group = false ) { + /* + * If nothing is passed, print the queue. If a string is passed, + * print that item. If an array is passed, print those items. + */ + $handles = false === $handles ? $this->queue : (array) $handles; + $this->all_deps( $handles ); + + $this->to_do_keyed_handles = array_flip( $this->to_do ); + + foreach ( $this->get_providers() as $provider_id => $provider ) { + // Bail out if the provider class does not exist. + if ( ! class_exists( $provider['class'] ) ) { + /* translators: %s is the provider name. */ + trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider_id ) ); + continue; + } + + $this->do_item( $provider_id, $group ); } - $this->providers[ $provider ] = $class; - return true; + + return $this->done; } /** - * Generate and enqueue webfonts styles. + * Invokes each provider to process and print its styles. + * + * @since 6.1.0 + * + * @see WP_Dependencies::do_item() * - * @since 6.0.0 + * @param string $provider_id The font family to process. + * @param int|false $group Not used. + * @return bool */ - public function generate_and_enqueue_styles() { - // Generate the styles. - $webfonts = $this->get_webfonts_by_provider( $this->get_enqueued_webfonts() ); - $styles = $this->generate_styles( $webfonts ); + public function do_item( $provider_id, $group = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + // Bail out if the provider is not registered. + if ( ! isset( $this->providers[ $provider_id ] ) ) { + return false; + } - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; + $fonts = $this->get_enqueued_fonts_for_provider( $provider_id ); + // If there are no web fonts for this provider, skip it. + if ( empty( $fonts ) ) { + return false; + } + + $provider_fonts = array(); + + foreach ( $fonts as $font_handle ) { + $provider_fonts[ $font_handle ] = $this->get_data( $font_handle, 'font-properties' ); } - // Enqueue the stylesheet. - wp_register_style( $this->stylesheet_handle, '' ); - wp_enqueue_style( $this->stylesheet_handle ); + $provider = new $this->providers[ $provider_id ]['class'](); + $provider->set_webfonts( $provider_fonts ); + $provider->print_styles(); - // Add the styles to the stylesheet. - wp_add_inline_style( $this->stylesheet_handle, $styles ); + foreach ( $fonts as $font_handle ) { + $this->done[] = $font_handle; + unset( + $this->to_do[ $this->to_do_keyed_handles[ $font_handle ] ], + $this->to_do_keyed_handles[ $font_handle ] + ); + } + + return true; } /** - * Generate and enqueue editor styles. + * Register a provider. + * + * @since 6.1.0 * - * @since 6.0.0 + * @param string $provider The provider name. + * @param string $class The provider class name. + * @return bool True if successfully registered, else false. */ - public function generate_and_enqueue_editor_styles() { - // Generate the styles. - $webfonts = $this->get_webfonts_by_provider( $this->get_all_webfonts() ); - $styles = $this->generate_styles( $webfonts ); - - // Bail out if there are no styles to enqueue. - if ( '' === $styles ) { - return; + public function register_provider( $provider, $class ) { + if ( empty( $provider ) || empty( $class ) || ! class_exists( $class ) ) { + return false; } - wp_enqueue_style( 'wp-block-library' ); - wp_add_inline_style( 'wp-block-library', $styles ); + $this->providers[ $provider ] = array( + 'class' => $class, + 'fonts' => array(), + ); + return true; } /** - * Generate styles for webfonts. + * Get the list of providers. * - * @since 6.0.0 + * @since 6.1.0 * - * @param array[] $webfonts_by_provider Webfonts organized by provider. - * @return string $styles Generated styles. + * @return WP_Webfonts_Provider[] All registered providers, each keyed by their unique ID. */ - private function generate_styles( array $webfonts_by_provider ) { - $styles = ''; - $providers = $this->get_providers(); - - /* - * Loop through each of the providers to get the CSS for their respective webfonts - * to incrementally generate the collective styles for all of them. - */ - foreach ( $providers as $provider_id => $provider_class ) { - - // Bail out if the provider class does not exist. - if ( ! class_exists( $provider_class ) ) { - /* translators: %s is the provider name. */ - trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider_id ) ); - continue; - } - - $provider_webfonts = isset( $webfonts_by_provider[ $provider_id ] ) - ? $webfonts_by_provider[ $provider_id ] - : array(); + public function get_providers() { + return $this->providers; + } - // If there are no registered webfonts for this provider, skip it. - if ( empty( $provider_webfonts ) ) { - continue; - } + /** + * Retrieves a list of enqueued web font variations for a provider. + * + * @since 6.1.0 + * + * @param string $provider_id Provider's unique ID. + * @return array[] Webfonts organized by providers. + */ + private function get_enqueued_fonts_for_provider( $provider_id ) { + $providers = $this->get_providers(); - /* - * Process the webfonts by first passing them to the provider via `set_webfonts()` - * and then getting the CSS from the provider. - */ - $provider = new $provider_class(); - $provider->set_webfonts( $provider_webfonts ); - $styles .= $provider->get_css(); + if ( empty( $providers[ $provider_id ] ) ) { + return array(); } - return $styles; + return array_intersect( + $providers[ $provider_id ]['fonts'], + $this->to_do + ); } - /** - * Reorganizes webfonts grouped by font-family into grouped by provider. + * Get the font slug. * - * @param array[] $font_families Font families and each of their webfonts. - * @return array[] Webfonts organized by providers. + * @since X.X.X + * @deprecated Use WP_Webfonts_Utils::convert_font_family_into_handle() + * + * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array + * or a font-family as a string. + * @return string|false The font slug on success, or false if the font-family cannot be determined. */ - private function get_webfonts_by_provider( array $font_families ) { - $providers = $this->get_providers(); - $webfonts_by_provider = array(); - - foreach ( $font_families as $webfonts ) { - foreach ( $webfonts as $webfont ) { - $provider = $webfont['provider']; - - // Skip if the provider is not registered. - if ( ! isset( $providers[ $provider ] ) ) { - /* translators: %s is the provider name. */ - trigger_error( sprintf( __( 'Webfont provider "%s" is not registered.', 'gutenberg' ), $provider ) ); - continue; - } + public static function get_font_slug( $to_convert ) { + _deprecated_function( + __METHOD__, + 'X.X.X', // Gutenberg version, not Core as this method will not be backported to Core. + 'Use WP_Webfonts_Utils::get_font_family_from_variation() to get the font family from an array and WP_Webfonts_Utils::convert_font_family_into_handle() to get the handle' + ); - // Initialize a new provider collection. - if ( ! isset( $webfonts_by_provider[ $provider ] ) ) { - $webfonts_by_provider[ $provider ] = array(); - } - $webfonts_by_provider[ $provider ][] = $webfont; - } - } + $font_family = is_array( $to_convert ) + ? WP_Webfonts_Utils::get_font_family_from_variation( $to_convert ) + : $to_convert; - return $webfonts_by_provider; + return WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ); } } diff --git a/lib/experimental/register-webfonts-from-theme-json.php b/lib/experimental/register-webfonts-from-theme-json.php index f0a37677eb0637..d0281708bbf137 100644 --- a/lib/experimental/register-webfonts-from-theme-json.php +++ b/lib/experimental/register-webfonts-from-theme-json.php @@ -5,173 +5,182 @@ * @package gutenberg */ -/** - * Register webfonts defined in theme.json. - */ -function gutenberg_register_webfonts_from_theme_json() { - // Get settings. - $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_settings(); - - // If in the editor, add webfonts defined in variations. - if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { - $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); - - foreach ( $variations as $variation ) { - - // Sanity check: Skip if fontFamilies are not defined in the variation. - if ( - empty( $variation['settings'] ) || - empty( $variation['settings']['typography'] ) || - empty( $variation['settings']['typography']['fontFamilies'] ) - ) { - continue; - } +if ( ! function_exists( 'gutenberg_register_webfonts_from_theme_json' ) ) { + /** + * Register webfonts defined in theme.json. + */ + function gutenberg_register_webfonts_from_theme_json() { + // Get settings. + $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_settings(); - // Merge the variation settings with the global settings. - $settings['typography'] = empty( $settings['typography'] ) ? array() : $settings['typography']; - $settings['typography']['fontFamilies'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']; - $settings['typography']['fontFamilies']['theme'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']['theme']; - $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] ); + // If in the editor, add webfonts defined in variations. + if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); - // Make sure there are no duplicates. - $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); - } - } + foreach ( $variations as $variation ) { - // Bail out early if there are no settings for webfonts. - if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { - return; - } - - $webfonts = array(); + // Sanity check: Skip if fontFamilies are not defined in the variation. + if ( empty( $variation['settings'] ) || empty( $variation['settings']['typography'] ) || empty( $variation['settings']['typography']['fontFamilies'] ) ) { + continue; + } - // Look for fontFamilies. - foreach ( $settings['typography']['fontFamilies'] as $font_families ) { - foreach ( $font_families as $font_family ) { + // Merge the variation settings with the global settings. + $settings['typography'] = empty( $settings['typography'] ) ? array() : $settings['typography']; + $settings['typography']['fontFamilies'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']; + $settings['typography']['fontFamilies']['theme'] = empty( $settings['typography']['fontFamilies'] ) ? array() : $settings['typography']['fontFamilies']['theme']; + $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] ); - // Skip if fontFace is not defined. - if ( empty( $font_family['fontFace'] ) ) { - continue; + // Make sure there are no duplicates. + $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] ); } + } - $font_family['fontFace'] = (array) $font_family['fontFace']; + // Bail out early if there are no settings for webfonts. + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { + return; + } - foreach ( $font_family['fontFace'] as $font_face ) { - // Skip if the webfont was registered through the Webfonts API. - if ( isset( $font_face['origin'] ) && 'gutenberg_wp_webfonts_api' === $font_face['origin'] ) { + $webfonts = array(); + $handles = array(); + + // Look for fontFamilies. + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $font_family ) { + + // Skip if fontFace is not defined. + if ( empty( $font_family['fontFace'] ) ) { continue; } - // Check if webfonts have a "src" param, and if they do account for the use of "file:./". - if ( ! empty( $font_face['src'] ) ) { - $font_face['src'] = (array) $font_face['src']; + $font_family['fontFace'] = (array) $font_family['fontFace']; + + foreach ( $font_family['fontFace'] as $font_face ) { + // Skip if the webfont was registered through the Webfonts API. + if ( isset( $font_face['origin'] ) && 'gutenberg_wp_webfonts_api' === $font_face['origin'] ) { + continue; + } + + // Check if webfonts have a "src" param, and if they do account for the use of "file:./". + if ( ! empty( $font_face['src'] ) ) { + $font_face['src'] = (array) $font_face['src']; - foreach ( $font_face['src'] as $src_key => $url ) { - // Tweak the URL to be relative to the theme root. - if ( ! str_starts_with( $url, 'file:./' ) ) { - continue; + foreach ( $font_face['src'] as $src_key => $url ) { + // Tweak the URL to be relative to the theme root. + if ( ! str_starts_with( $url, 'file:./' ) ) { + continue; + } + $font_face['src'][ $src_key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); } - $font_face['src'][ $src_key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) ); } - } - // Convert keys to kebab-case. - foreach ( $font_face as $property => $value ) { - $kebab_case = _wp_to_kebab_case( $property ); - $font_face[ $kebab_case ] = $value; - if ( $kebab_case !== $property ) { - unset( $font_face[ $property ] ); + // Convert keys to kebab-case. + foreach ( $font_face as $property => $value ) { + $kebab_case = _wp_to_kebab_case( $property ); + $font_face[ $kebab_case ] = $value; + if ( $kebab_case !== $property ) { + unset( $font_face[ $property ] ); + } + } + + $font_family = WP_Webfonts_Utils::get_font_family_from_variation( $font_face ); + if ( empty( $font_family_handle ) ) { + $font_family = $font_family['slug']; + } + $font_family_handle = WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ); + if ( is_null( $font_family_handle ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Font family not defined in the variation or "slug".', 'gutenberg' ), '6.1.0' ); + } + + $handles[] = $font_family_handle; + if ( ! array_key_exists( $font_family_handle, $webfonts ) ) { + $webfonts[ $font_family_handle ] = array(); } - } - $webfonts[] = $font_face; + $webfonts[ $font_family_handle ][] = $font_face; + } } } - } - foreach ( $webfonts as $webfont ) { - wp_webfonts()->register_webfont( $webfont ); - } - foreach ( $webfonts as $webfont ) { - wp_webfonts()->enqueue_webfont( $webfont['font-family'] ); - } -} -/** - * Add missing fonts data to the global styles. - * - * @param array $data The global styles. - * @return array The global styles with missing fonts data. - */ -function gutenberg_add_registered_webfonts_to_theme_json( $data ) { - $font_families_registered = wp_webfonts()->get_all_webfonts(); - $font_families_from_theme = array(); - if ( ! empty( $data['settings'] ) && ! empty( $data['settings']['typography'] ) && ! empty( $data['settings']['typography']['fontFamilies'] ) ) { - $font_families_from_theme = $data['settings']['typography']['fontFamilies']; + wp_register_webfonts( $webfonts ); + wp_enqueue_webfont( $handles ); } +} +if ( ! function_exists( 'gutenberg_add_registered_webfonts_to_theme_json' ) ) { /** - * Helper to get an array of the font-families. + * Add missing fonts data to the global styles. * - * @param array $families_data The font-families data. - * @return array The font-families array. + * @param array $data The global styles. + * @return array The global styles with missing fonts data. */ - $get_families = static function( $families_data ) { - $families = array(); - foreach ( $families_data as $family ) { - $families[] = WP_Webfonts::get_font_slug( $family ); + function gutenberg_add_registered_webfonts_to_theme_json( $data ) { + $font_families_registered = wp_webfonts()->get_registered(); + $font_families_from_theme = array(); + if ( ! empty( $data['settings'] ) && ! empty( $data['settings']['typography'] ) && ! empty( $data['settings']['typography']['fontFamilies'] ) ) { + $font_families_from_theme = $data['settings']['typography']['fontFamilies']; } - // Micro-optimization: Use array_flip( array_flip( $array ) ) - // instead of array_unique( $array ) because it's faster. - // The result is the same. - return array_flip( array_flip( $families ) ); - }; + /** + * Helper to get an array of the font-families. + * + * @param array $families_data The font-families data. + * @return array The font-families array. + */ + $get_families = static function ( $families_data ) { + $families = array(); + foreach ( $families_data as $family ) { + $families[] = WP_Webfonts::get_font_slug( $family ); + } - // Diff the arrays to find the missing fonts. - $to_add = array_diff( - array_keys( $font_families_registered ), - $get_families( $font_families_from_theme ) - ); + // Micro-optimization: Use array_flip( array_flip( $array ) ) + // instead of array_unique( $array ) because it's faster. + // The result is the same. + return array_flip( array_flip( $families ) ); + }; - // Bail out early if there are no missing fonts. - if ( empty( $to_add ) ) { - return $data; - } + // Diff the arrays to find the missing fonts. + $to_add = array_diff( array_keys( $font_families_registered ), $get_families( $font_families_from_theme ) ); - // Make sure the path to settings.typography.fontFamilies.theme exists - // before adding missing fonts. - if ( empty( $data['settings'] ) ) { - $data['settings'] = array(); - } - if ( empty( $data['settings']['typography'] ) ) { - $data['settings']['typography'] = array(); - } - if ( empty( $data['settings']['typography']['fontFamilies'] ) ) { - $data['settings']['typography']['fontFamilies'] = array(); - } + // Bail out early if there are no missing fonts. + if ( empty( $to_add ) ) { + return $data; + } + + // Make sure the path to settings.typography.fontFamilies.theme exists + // before adding missing fonts. + if ( empty( $data['settings'] ) ) { + $data['settings'] = array(); + } + if ( empty( $data['settings']['typography'] ) ) { + $data['settings']['typography'] = array(); + } + if ( empty( $data['settings']['typography']['fontFamilies'] ) ) { + $data['settings']['typography']['fontFamilies'] = array(); + } - foreach ( $to_add as $slug ) { - $font_faces_for_family = $font_families_registered[ $slug ]; - $family_name = $font_faces_for_family[0]['font-family']; - $font_faces = array(); + foreach ( $to_add as $slug ) { + $font_faces_for_family = $font_families_registered[ $slug ]; + $family_name = $font_faces_for_family[0]['font-family']; + $font_faces = array(); - foreach ( $font_faces_for_family as $font_face ) { - $camel_cased = array( 'origin' => 'gutenberg_wp_webfonts_api' ); - foreach ( $font_face as $key => $value ) { - $camel_cased[ lcfirst( str_replace( '-', '', ucwords( $key, '-' ) ) ) ] = $value; + foreach ( $font_faces_for_family as $font_face ) { + $camel_cased = array( 'origin' => 'gutenberg_wp_webfonts_api' ); + foreach ( $font_face as $key => $value ) { + $camel_cased[ lcfirst( str_replace( '-', '', ucwords( $key, '-' ) ) ) ] = $value; + } + $font_faces[] = $camel_cased; } - $font_faces[] = $camel_cased; + + $data['settings']['typography']['fontFamilies'][] = array( + 'fontFamily' => str_contains( $family_name, ' ' ) ? "'{$family_name}'" : $family_name, + 'name' => $family_name, + 'slug' => $slug, + 'fontFace' => $font_faces, + ); } - $data['settings']['typography']['fontFamilies'][] = array( - 'fontFamily' => str_contains( $family_name, ' ' ) ? "'{$family_name}'" : $family_name, - 'name' => $family_name, - 'slug' => $slug, - 'fontFace' => $font_faces, - ); + return $data; } - - return $data; } // `gutenberg_register_webfonts_from_theme_json()` calls `WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()`, which instantiates `WP_Theme_JSON_Gutenberg()`; diff --git a/lib/experimental/webfonts.php b/lib/experimental/webfonts.php index 201101d1d7093e..81057b95b02bc7 100644 --- a/lib/experimental/webfonts.php +++ b/lib/experimental/webfonts.php @@ -9,152 +9,203 @@ if ( ! function_exists( 'wp_webfonts' ) ) { /** - * Instantiates the webfonts controller, if not already set, and returns it. + * Initialize $wp_webfonts if it has not been set. * - * @since 6.0.0 + * @since 6.1.0 + * + * @global WP_Webfonts $wp_webfonts * - * @return WP_Webfonts Instance of the controller. + * @return WP_Webfonts WP_Webfonts instance. */ function wp_webfonts() { global $wp_webfonts; - if ( ! $wp_webfonts instanceof WP_Webfonts ) { + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { $wp_webfonts = new WP_Webfonts(); - $wp_webfonts->init(); + $wp_webfonts->register_provider( 'local', 'WP_Webfonts_Provider_Local' ); } return $wp_webfonts; } } +if ( ! function_exists( 'wp_register_font_family' ) ) { + /** + * Registers a font family. + * + * @since 6.1.0 + * + * @param string $font_family The font family's name or handle to register. + * @return string|null The font family handle when successfully registered. Else null. + */ + function wp_register_font_family( $font_family ) { + $handle = WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ); + if ( ! $handle ) { + return null; + } + + $result = wp_webfonts()->add( $handle, false ) ? $handle : null; + return $result ? $handle : null; + } +} + +if ( ! function_exists( 'wp_register_webfont_variation' ) ) { + /** + * Registers a variation to the given font family. + * + * @since 6.1.0 + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. When none is provided, the + * handle will be dynamically generated. + * Default empty string. + * @return string|null Variation handle on success. Else null. + */ + function wp_register_webfont_variation( $font_family_handle, array $variation, $variation_handle = '' ) { + return wp_webfonts()->add_variation( $font_family_handle, $variation, $variation_handle ); + } +} + if ( ! function_exists( 'wp_register_webfonts' ) ) { /** - * Registers a collection of webfonts. - * - * Example of how to register Source Serif Pro font with font-weight range of 200-900 - * and font-style of normal and italic: - * - * If the font files are contained within the theme: - * - * wp_register_webfonts( - * array( - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'normal', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ), - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'italic', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), - * ), - * ) - * ); - * - * - * Webfonts should be registered in the `after_setup_theme` hook. + * Registers a list of web fonts and variations. * - * @since 6.0.0 + * @since 6.1.0 * - * @param array[] $webfonts Webfonts to be registered. - * This contains an array of webfonts to be registered. - * Each webfont is an array. - * @return string[] The font family slug of the registered webfonts. + * @param array[] $webfonts Web fonts to be registered. + * @return string[] The font family handle of the registered web fonts. */ function wp_register_webfonts( array $webfonts ) { - $registered_webfont_slugs = array(); + $registered = array(); - foreach ( $webfonts as $webfont ) { - $slug = wp_register_webfont( $webfont ); + foreach ( $webfonts as $font_family => $variations ) { + /* + * Handle the deprecated web font's structure but alert developers + * to modify their web font structure to group and key by font family. + * + * Note: This code block will not be backported to Core. + */ + if ( ! WP_Webfonts_Utils::is_defined( $font_family ) ) { + _doing_it_wrong( + __FUNCTION__, + __( 'Variations must be grouped and keyed by their font family.', 'gutenberg' ), + '6.1.0' + ); + $font_family_handle = wp_register_webfont( $variations ); + if ( ! $font_family_handle ) { + continue; + } + $registered[ $font_family_handle ] = true; + continue; + } // end of code block for deprecated web fonts structure. + + $font_family_handle = wp_register_font_family( $font_family ); + if ( ! $font_family_handle ) { + continue; + } - if ( is_string( $slug ) ) { - $registered_webfont_slugs[ $slug ] = true; + // Register each of the variations for this font family. + foreach ( $variations as $handle => $variation ) { + wp_register_webfont( + $variation, + $font_family_handle, + WP_Webfonts_Utils::is_defined( $handle ) ? $handle : '' + ); } + + $registered[] = $font_family_handle; } - return array_keys( $registered_webfont_slugs ); + return $registered; } } if ( ! function_exists( 'wp_register_webfont' ) ) { /** - * Registers a single webfont. - * - * Example of how to register Source Serif Pro font with font-weight range of 200-900: - * - * If the font file is contained within the theme: - * - * - * wp_register_webfont( - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'normal', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ) - * ); - * + * Registers a single web font. * - * @since 6.0.0 + * @since 6.1.0 * - * @param array $webfont Webfont to be registered. - * @return string|false The font family slug if successfully registered, else false. + * @param array $variation Array of variation properties to be registered. + * @param string $font_family_handle Optional. Font family handle for the given variation. + * Default empty string. + * @param string $variation_handle Optional. Handle for the variation to register. + * @return string|null The font family slug if successfully registered. Else null. */ - function wp_register_webfont( array $webfont ) { - return wp_webfonts()->register_webfont( $webfont ); + function wp_register_webfont( array $variation, $font_family_handle = '', $variation_handle = '' ) { + // When font family's handle is not passed, attempt to get it from the variation. + if ( ! WP_Webfonts_Utils::is_defined( $font_family_handle ) ) { + $font_family = WP_Webfonts_Utils::get_font_family_from_variation( $variation ); + if ( $font_family ) { + $font_family_handle = WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ); + } + } + + if ( empty( $font_family_handle ) ) { + trigger_error( 'Font family handle must be a non-empty string.' ); + return null; + } + + return wp_webfonts()->add_variation( $font_family_handle, $variation, $variation_handle ) + ? $font_family_handle + : null; } } -if ( ! function_exists( 'wp_enqueue_webfonts' ) ) { +if ( ! function_exists( 'wp_enqueue_webfont' ) ) { /** - * Enqueues a collection of font families. - * - * Example of how to enqueue Source Serif Pro and Roboto font families, both registered beforehand. + * Enqueues one or more font family and all of its variations. * - * - * wp_enqueue_webfonts( - * 'Roboto', - * 'Sans Serif Pro' - * ); - * - * - * Font families should be enqueued from the `init` hook or later. + * @param string|string[] $font_family Font family handle (string) or handles (array of strings). + */ + function wp_enqueue_webfont( $font_family ) { + wp_webfonts()->enqueue( $font_family ); + } +} + +if ( ! function_exists( 'wp_enqueue_webfont_variations' ) ) { + /** + * Enqueues a given set of web font variations. * - * @since 6.0.0 + * @TODO Is this needed? * - * @param string[] $webfonts Font families to be enqueued. + * @param string[] $variations Font variations. */ - function wp_enqueue_webfonts( array $webfonts ) { - foreach ( $webfonts as $webfont ) { - wp_enqueue_webfont( $webfont ); + function wp_enqueue_webfont_variations( $variations ) { + $wp_webfonts = wp_webfonts(); + + // Looking to enqueue all variations of a font family. + foreach ( $variations as $variation ) { + $wp_webfonts->enqueue( $variation ); } } } -if ( ! function_exists( 'wp_enqueue_webfont' ) ) { +if ( ! function_exists( 'wp_deregister_font_family' ) ) { /** - * Enqueue a single font family that has been registered beforehand. - * - * Example of how to enqueue Source Serif Pro font: + * Deregisters a font family and all registered variations. * - * - * wp_enqueue_webfont( 'Source Serif Pro' ); - * + * @since 6.1.0 * - * Font families should be enqueued from the `init` hook or later. + * @param string $font_family_handle The font family to remove. + */ + function wp_deregister_font_family( $font_family_handle ) { + wp_webfonts()->remove_font_family( $font_family_handle ); + } +} + +if ( ! function_exists( 'wp_deregister_webfont_variation' ) ) { + /** + * Deregisters a font variation. * - * @since 6.0.0 + * @since 6.1.0 * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. + * @param string $font_family_handle The font family for this variation. + * @param string $variation_handle The variation's handle to remove. */ - function wp_enqueue_webfont( $font_family_name ) { - return wp_webfonts()->enqueue_webfont( $font_family_name ); + function wp_deregister_webfont_variation( $font_family_handle, $variation_handle ) { + wp_webfonts()->remove_variation( $font_family_handle, $variation_handle ); } } @@ -211,6 +262,40 @@ function wp_get_webfont_providers() { } } +if ( ! function_exists( 'wp_print_webfonts' ) ) { + /** + * Invokes each provider to process and print its styles. + * + * @since 6.1.0 + * + * @param string|string[]|false $handles Optional. Items to be processed: queue (false), + * single item (string), or multiple items (array of strings). + * Default false. + * @return array|string[] Array of web font handles that have been processed. + * An empty array if none were processed. + */ + function wp_print_webfonts( $handles = false ) { + global $wp_webfonts; + + if ( '' === $handles ) { // For 'wp_head'. + $handles = false; + } + + _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ ); + + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { + if ( ! $handles ) { + return array(); // No need to instantiate if nothing is there. + } + } + + return wp_webfonts()->do_items( $handles ); + } +} + +add_action( 'admin_print_styles', 'wp_print_webfonts' ); +add_action( 'wp_head', 'wp_print_webfonts' ); + /** * Add webfonts mime types. */ diff --git a/lib/load.php b/lib/load.php index 9600d3100d3b7e..9f274ab9dc2eb3 100644 --- a/lib/load.php +++ b/lib/load.php @@ -96,6 +96,7 @@ function gutenberg_is_experiment_enabled( $name ) { // Experimental features. remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API. require __DIR__ . '/experimental/block-editor-settings-mobile.php'; +require __DIR__ . '/experimental/class-wp-webfonts-utils.php'; require __DIR__ . '/experimental/register-webfonts-from-theme-json.php'; require __DIR__ . '/experimental/class-wp-theme-json-gutenberg.php'; require __DIR__ . '/experimental/class-wp-theme-json-resolver-gutenberg.php'; diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 33533fc872f699..f518c4f442bd01 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -491,6 +491,11 @@ protected static function get_css_declarations( $style_value, $style_definition, // If the input contains an array, assume box model-like properties // for styles such as margins and padding. if ( is_array( $style_value ) ) { + // Bail out early if the `'individual'` property is not defined. + if ( ! isset( $style_property_keys['individual'] ) ) { + return $css_declarations; + } + foreach ( $style_value as $key => $value ) { if ( is_string( $value ) && strpos( $value, 'var:' ) !== false && ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) { $value = static::get_css_var_value( $value, $style_definition['css_vars'] ); diff --git a/phpcs.xml.dist b/phpcs.xml.dist index ce2c3a67a6a423..59c87341f6eb29 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -87,5 +87,14 @@ ./packages/block-serialization-default-parser/parser.php + /phpunit/* + + + + + /phpunit/* + + + /phpunit/* diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 65ef5a17e8b9d5..2f6e997b45b086 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -5,6 +5,7 @@ convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" + convertDeprecationsToExceptions="true" > diff --git a/phpunit/blocks/render-last-posts-test.php b/phpunit/blocks/render-last-posts-test.php index 49734adb515fdc..a20b6f717a4fa3 100644 --- a/phpunit/blocks/render-last-posts-test.php +++ b/phpunit/blocks/render-last-posts-test.php @@ -24,6 +24,10 @@ class Tests_Blocks_RenderLastPosts extends WP_UnitTestCase { * @var array */ protected static $attachment_ids; + /** + * @var array|null + */ + private $original_block_supports; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { self::$sticky_post = $factory->post->create_and_get( @@ -49,6 +53,21 @@ public static function wpTearDownAfterClass() { } } + public function set_up() { + parent::set_up(); + + $this->original_block_supports = WP_Block_Supports::$block_to_render; + WP_Block_Supports::$block_to_render = array( + 'attrs' => array(), + 'blockName' => '', + ); + } + + public function tear_down() { + WP_Block_Supports::$block_to_render = $this->original_block_supports; + parent::tear_down(); + } + /** * @covers ::render_block_core_latest_posts */ @@ -56,10 +75,13 @@ public function test_render_block_core_latest_posts() { $action = new MockAction(); add_filter( 'update_post_metadata_cache', array( $action, 'filter' ), 10, 2 ); $attributes = array( - 'displayFeaturedImage' => true, - 'postsToShow' => 5, - 'orderBy' => 'date', - 'order' => 'DESC', + 'displayFeaturedImage' => true, + 'postsToShow' => 5, + 'orderBy' => 'date', + 'order' => 'DESC', + 'excerptLength' => 0, + 'featuredImageSizeSlug' => '', + 'addLinkToFeaturedImage' => false, ); gutenberg_render_block_core_latest_posts( $attributes ); @@ -75,10 +97,13 @@ public function test_render_block_core_latest_posts_no_priming() { $action = new MockAction(); add_filter( 'update_post_metadata_cache', array( $action, 'filter' ), 10, 2 ); $attributes = array( - 'displayFeaturedImage' => false, - 'postsToShow' => 5, - 'orderBy' => 'date', - 'order' => 'DESC', + 'displayFeaturedImage' => false, + 'postsToShow' => 5, + 'orderBy' => 'date', + 'order' => 'DESC', + 'excerptLength' => 0, + 'featuredImageSizeSlug' => '', + 'addLinkToFeaturedImage' => false, ); gutenberg_render_block_core_latest_posts( $attributes ); diff --git a/phpunit/bootstrap.php b/phpunit/bootstrap.php index 9423c5a1efc5c3..fac777864200a8 100644 --- a/phpunit/bootstrap.php +++ b/phpunit/bootstrap.php @@ -5,6 +5,23 @@ * @package Gutenberg */ +// Debug settings for parity with WordPress Core's PHPUnit tests. +if ( ! defined( 'WP_DEBUG' ) ) { + define( 'WP_DEBUG', true ); +} +if ( ! defined( 'LOCAL_WP_DEBUG_LOG' ) ) { + define( 'LOCAL_WP_DEBUG_LOG', true ); +} +if ( ! defined( 'LOCAL_WP_DEBUG_DISPLAY' ) ) { + define( 'LOCAL_WP_DEBUG_DISPLAY', true ); +} +if ( ! defined( 'LOCAL_SCRIPT_DEBUG' ) ) { + define( 'LOCAL_SCRIPT_DEBUG', true ); +} +if ( ! defined( 'LOCAL_WP_ENVIRONMENT_TYPE' ) ) { + define( 'LOCAL_WP_ENVIRONMENT_TYPE', 'local' ); +} + // Require composer dependencies. require_once dirname( __DIR__ ) . '/vendor/autoload.php'; diff --git a/phpunit/class-block-library-navigation-link-test.php b/phpunit/class-block-library-navigation-link-test.php index 982ed393ea0b2e..7b91742ec22724 100644 --- a/phpunit/class-block-library-navigation-link-test.php +++ b/phpunit/class-block-library-navigation-link-test.php @@ -18,6 +18,10 @@ class Block_Library_Navigation_Link_Test extends WP_UnitTestCase { private static $pages; private static $terms; + /** + * @var array|null + */ + private $original_block_supports; public static function wpSetUpBeforeClass() { @@ -90,6 +94,21 @@ public static function wpTearDownAfterClass() { } } + public function set_up() { + parent::set_up(); + + $this->original_block_supports = WP_Block_Supports::$block_to_render; + WP_Block_Supports::$block_to_render = array( + 'attrs' => array(), + 'blockName' => '', + ); + } + + public function tear_down() { + WP_Block_Supports::$block_to_render = $this->original_block_supports; + parent::tear_down(); + } + function test_returns_link_when_post_is_published() { $page_id = self::$page->ID; diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 34d9905743c61e..593b66374e6321 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -736,66 +736,7 @@ function test_set_spacing_sizes( $spacing_scale, $expected_output ) { */ function data_generate_spacing_scale_fixtures() { return array( - 'empty_spacing_scale' => array( - 'spacing_scale' => array(), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_missing_operator' => array( - 'spacingScale' => array( - 'operator' => '', - 'increment' => 1.5, - 'steps' => 1, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_increment' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 'add two to previous value', - 'steps' => 1, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_steps' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 'spiral staircase preferred', - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_medium_step' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 5, - 'mediumStep' => 'That which is just right', - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_missing_unit' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 5, - 'mediumStep' => 4, - ), - 'expected_output' => null, - ), - - 'one_step_spacing_scale' => array( + 'one_step_spacing_scale' => array( 'spacingScale' => array( 'operator' => '+', 'increment' => 1.5, @@ -1006,6 +947,94 @@ function data_generate_spacing_scale_fixtures() { ); } + /** + * Tests generating the spacing presets array based on the spacing scale provided. + * + * @dataProvider data_set_spacing_sizes_when_invalid + */ + public function test_set_spacing_sizes_when_invalid( $spacing_scale, $expected_output ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ); + + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'settings' => array( + 'spacing' => array( + 'spacingScale' => $spacing_scale, + ), + ), + ) + ); + + $theme_json->set_spacing_sizes(); + $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); + } + + /** + * Data provider for spacing scale tests. + * + * @return array + */ + function data_set_spacing_sizes_when_invalid() { + return array( + + 'invalid_spacing_scale_values_missing_operator' => array( + 'spacingScale' => array( + 'operator' => '', + 'increment' => 1.5, + 'steps' => 1, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_increment' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 'add two to previous value', + 'steps' => 1, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_steps' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 'spiral staircase preferred', + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_medium_step' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 5, + 'mediumStep' => 'That which is just right', + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_missing_unit' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 5, + 'mediumStep' => 4, + ), + 'expected_output' => null, + ), + ); + } + function test_get_styles_for_block_with_padding_aware_alignments() { $theme_json = new WP_Theme_JSON_Gutenberg( array( diff --git a/phpunit/class-wp-webfonts-test.php b/phpunit/class-wp-webfonts-test.php deleted file mode 100644 index 97e2762df4b568..00000000000000 --- a/phpunit/class-wp-webfonts-test.php +++ /dev/null @@ -1,451 +0,0 @@ -old_wp_webfonts = $wp_webfonts; - - $wp_webfonts = null; - } - - public function tear_down() { - global $wp_webfonts; - - $wp_webfonts = $this->old_wp_webfonts; - parent::tear_down(); - } - - /** - * Test wp_register_webfonts() bulk register webfonts. - * - * @covers wp_register_webfonts - * @covers WP_Webfonts::register_font - * @covers WP_Webfonts::get_registered_fonts - * @covers WP_Webfonts::get_enqueued_fonts - */ - public function test_wp_register_webfonts() { - $font_family_name = 'Source Serif Pro'; - - $fonts = array( - array( - 'provider' => 'local', - 'font-family' => $font_family_name, - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - array( - 'provider' => 'local', - 'font-family' => $font_family_name, - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'font-display' => 'fallback', - ), - ); - - $expected = array( - wp_webfonts()->get_font_slug( $font_family_name ) => $fonts, - ); - - wp_register_webfonts( $fonts ); - $this->assertEquals( $expected, wp_webfonts()->get_registered_webfonts() ); - $this->assertEquals( array(), wp_webfonts()->get_enqueued_webfonts() ); - } - - /** - * Test wp_register_webfont() register a single webfont. - * - * @covers wp_register_webfont - * @covers WP_Webfonts::register_font - * @covers WP_Webfonts::get_registered_fonts - * @covers WP_Webfonts::get_enqueued_fonts - */ - public function test_wp_register_webfont() { - $font_family_name = 'Source Serif Pro'; - - $font = array( - 'provider' => 'local', - 'font-family' => $font_family_name, - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ); - - $expected = array( - wp_webfonts()->get_font_slug( $font_family_name ) => array( $font ), - ); - - wp_register_webfont( $font ); - $this->assertEquals( $expected, wp_webfonts()->get_registered_webfonts() ); - $this->assertEquals( array(), wp_webfonts()->get_enqueued_webfonts() ); - } - - /** - * Test wp_register_webfont() does not enqueue the webfont on registration. - * - * @covers wp_register_webfont - * @covers WP_Webfonts::register_font - * @covers WP_Webfonts::get_registered_fonts - * @covers WP_Webfonts::get_enqueued_fonts - */ - public function test_wp_register_webfont_does_not_enqueue_on_registration() { - $font_family_name = 'Source Serif Pro'; - - $font = array( - 'provider' => 'local', - 'font-family' => $font_family_name, - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ); - - $expected = array( - wp_webfonts()->get_font_slug( $font_family_name ) => array( $font ), - ); - - wp_register_webfont( $font ); - $this->assertEquals( $expected, wp_webfonts()->get_registered_webfonts() ); - $this->assertEquals( array(), wp_webfonts()->get_enqueued_webfonts() ); - } - - /** - * Test wp_enqueue_webfonts() bulk enqueue webfonts. - * - * @covers wp_enqueue_webfonts - * @covers WP_Webfonts::enqueue_font - * @covers WP_Webfonts::get_enqueued_fonts - * @covers WP_Webfonts::get_registered_fonts - */ - public function test_wp_enqueue_webfonts() { - $source_serif_pro = array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ); - - wp_register_webfont( $source_serif_pro ); - - $roboto = array( - 'provider' => 'local', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/roboto/Roboto.ttf.woff2', - 'font-display' => 'fallback', - ); - - wp_register_webfont( $roboto ); - - $expected = array( - wp_webfonts()->get_font_slug( $source_serif_pro['font-family'] ) => array( $source_serif_pro ), - wp_webfonts()->get_font_slug( $roboto['font-family'] ) => array( $roboto ), - ); - - wp_enqueue_webfonts( - array( - $source_serif_pro['font-family'], - $roboto['font-family'], - ) - ); - - $this->assertEquals( $expected, wp_webfonts()->get_enqueued_webfonts() ); - $this->assertEquals( array(), wp_webfonts()->get_registered_webfonts() ); - } - - /** - * Test wp_enqueue_font() enqueues a registered webfont. - * - * @covers wp_enqueue_webfont - * @covers WP_Webfonts::enqueued_font - * @covers WP_Webfonts::get_enqueued_fonts - * @covers WP_Webfonts::get_registered_fonts - */ - public function test_wp_enqueue_webfont_enqueues_registered_webfont() { - $font_family_name = 'Source Serif Pro'; - - $font = array( - 'provider' => 'local', - 'font-family' => $font_family_name, - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ); - - $expected = array( - wp_webfonts()->get_font_slug( $font_family_name ) => array( $font ), - ); - - wp_register_webfont( $font ); - wp_enqueue_webfont( $font_family_name ); - - $this->assertEquals( array(), wp_webfonts()->get_registered_webfonts() ); - $this->assertEquals( $expected, wp_webfonts()->get_enqueued_webfonts() ); - } - - /** - * Test wp_enqueue_font() does not enqueue a webfont that was not registered. - * - * @covers wp_enqueue_webfont - * @covers WP_Webfonts::enqueued_font - * @covers WP_Webfonts::get_enqueued_fonts - * @covers WP_Webfonts::get_registered_fonts - * - * @expectedIncorrectUsage WP_Webfonts::enqueue_webfont - */ - public function test_wp_enqueue_webfont_does_not_enqueue_unregistered_webfont() { - $font_family_name = 'Source Serif Pro'; - - wp_enqueue_webfont( $font_family_name ); - - $this->assertSame( array(), wp_webfonts()->get_registered_webfonts(), 'WP_Webfonts::get_registered_webfonts should return an empty array' ); - $this->assertSame( array(), wp_webfonts()->get_enqueued_webfonts(), 'WP_Webfonts::get_enqueued_webfonts should return an empty array' ); - } - - /** - * @covers wp_register_webfont - * @covers WP_Webfonts::register_provider - * @covers WP_Webfonts::get_providers - */ - public function test_get_providers() { - wp_register_webfont_provider( 'test-provider', 'Test_Provider' ); - $this->assertEquals( - array( - 'local' => 'WP_Webfonts_Provider_Local', - 'test-provider' => 'Test_Provider', - ), - wp_get_webfont_providers() - ); - } - - /** - * @dataProvider data_get_font_slug_when_cannot_determine_fontfamily - * @covers WP_Webfonts::get_font_slug - * - * @expectedIncorrectUsage WP_Webfonts::get_font_slug - */ - public function test_get_font_slug_when_cannot_determine_fontfamily( $to_convert ) { - $this->assertFalse( wp_webfonts()->get_font_slug( $to_convert ) ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_get_font_slug_when_cannot_determine_fontfamily() { - return array( - 'empty array' => array( array() ), - 'array without a font-family key' => array( - array( - 'provider' => 'local', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - ), - "array with 'font family' key" => array( - array( 'font family' => 'Source Serif Pro' ), - ), - "array with 'Font-Family' key" => array( - array( 'Font-Family' => 'Source Serif Pro' ), - ), - "array with 'FontFamily' key" => array( - array( 'FontFamily' => 'Source Serif Pro' ), - ), - ); - } - - /** - * @dataProvider data_get_font_slug - * @covers WP_Webfonts::get_font_slug - */ - public function test_get_font_slug( $to_convert, $expected ) { - $this->assertSame( $expected, wp_webfonts()->get_font_slug( $to_convert ) ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_get_font_slug() { - return array( - "array using 'fontFamily' format" => array( - 'to_convert' => array( 'fontFamily' => 'Source Serif Pro' ), - 'expected' => 'source-serif-pro', - ), - "array using 'font-family' format" => array( - 'to_convert' => array( 'font-family' => 'Source Serif Pro' ), - 'expected' => 'source-serif-pro', - ), - 'string with font family name' => array( - 'to_convert' => 'Source Serif Pro', - 'expected' => 'source-serif-pro', - ), - 'empty string' => array( - 'to_convert' => '', - 'expected' => '', - ), - ); - } - - /** - * @covers WP_Webfonts::validate_webfont - */ - public function test_validate_webfont() { - // Test empty array. - $this->assertFalse( wp_webfonts()->validate_webfont( array() ) ); - - $font = array( - 'font-family' => 'Test Font 1', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ); - - // Test missing provider fallback to local. - $this->assertEquals( 'local', wp_webfonts()->validate_webfont( $font )['provider'] ); - - // Test missing font-weight fallback to 400. - $this->assertEquals( '400', wp_webfonts()->validate_webfont( $font )['font-weight'] ); - - // Test missing font-style fallback to normal. - $this->assertEquals( 'normal', wp_webfonts()->validate_webfont( $font )['font-style'] ); - - // Test missing font-display fallback to fallback. - $this->assertEquals( 'fallback', wp_webfonts()->validate_webfont( $font )['font-display'] ); - - // Test local font with missing "src". - $this->assertFalse( wp_webfonts()->validate_webfont( array( 'font-family' => 'Test Font 2' ) ) ); - - // Test valid src URL, without a protocol. - $font['src'] = '//example.com/SourceSerif4Variable-Roman.ttf.woff2'; - $this->assertEquals( wp_webfonts()->validate_webfont( $font )['src'], $font['src'] ); - - // Test font-weight. - $font_weights = array( 100, '100', '100 900', 'normal' ); - foreach ( $font_weights as $value ) { - $font['font-weight'] = $value; - $this->assertEquals( wp_webfonts()->validate_webfont( $font )['font-weight'], $value ); - } - - // Test that invalid keys get removed from the font. - $font['invalid-key'] = 'invalid'; - $this->assertArrayNotHasKey( 'invalid-key', wp_webfonts()->validate_webfont( $font ) ); - } - - /** - * Test generate_and_enqueue_styles outputs only enqueued webfonts. - * - * @covers WP_Webfonts::generate_and_enqueue_styles - */ - public function test_generate_and_enqueue_styles() { - wp_register_webfonts( - array( - array( - 'provider' => 'local', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/roboto/Roboto-Regular.ttf', - 'font-display' => 'fallback', - ), - array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - ) - ); - - wp_enqueue_webfont( 'Source Serif Pro' ); - - wp_webfonts()->generate_and_enqueue_styles(); - - $expected = <<assertStringContainsString( - $expected, - get_echo( 'wp_print_styles' ) - ); - } - - /** - * Test generate_and_enqueue_editor_styles outputs registered and enqueued webfonts. - * Both are necessary so the editor correctly loads webfonts picked while in the editor. - * - * @covers WP_Webfonts::generate_and_enqueue_editor_styles - */ - public function test_generate_and_enqueue_editor_styles() { - wp_register_webfonts( - array( - array( - 'provider' => 'local', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/roboto/Roboto-Regular.ttf', - 'font-display' => 'fallback', - ), - array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - ) - ); - - wp_enqueue_webfont( 'Source Serif Pro' ); - - wp_webfonts()->generate_and_enqueue_editor_styles(); - - $expected = <<assertStringContainsString( - $expected, - get_echo( 'wp_print_styles' ) - ); - } -} diff --git a/phpunit/fixtures/mock-provider.php b/phpunit/fixtures/mock-provider.php new file mode 100644 index 00000000000000..152badb1ce266c --- /dev/null +++ b/phpunit/fixtures/mock-provider.php @@ -0,0 +1,21 @@ +'; + + public function get_css() { + return $this->css; + } +} diff --git a/phpunit/webfonts/wp-webfonts-testcase.php b/phpunit/webfonts/wp-webfonts-testcase.php new file mode 100644 index 00000000000000..495ef996a7e51b --- /dev/null +++ b/phpunit/webfonts/wp-webfonts-testcase.php @@ -0,0 +1,137 @@ +old_wp_webfonts = $GLOBALS['wp_webfonts']; + $GLOBALS['wp_webfonts'] = null; + } + + public function tear_down() { + $GLOBALS['wp_webfonts'] = $this->old_wp_webfonts; + parent::tear_down(); + } + + protected function set_up_mock( $method ) { + if ( is_string( $method ) ) { + $method = array( $method ); + } + $mock = $this->getMockBuilder( WP_Webfonts::class )->setMethods( $method )->getMock(); + + // Set the global. + $GLOBALS['wp_webfonts'] = $mock; + + return $mock; + } + + protected function get_registered_handles() { + return array_keys( $this->get_registered() ); + } + + protected function get_registered() { + return wp_webfonts()->registered; + } + + protected function get_variations( $font_family, $wp_webfonts = null ) { + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { + $wp_webfonts = wp_webfonts(); + } + return $wp_webfonts->registered[ $font_family ]->deps; + } + + protected function get_enqueued_handles() { + return wp_webfonts()->queue; + } + + protected function get_queued_before_register( $wp_webfonts = null ) { + return $this->get_property_value( 'queued_before_register', WP_Dependencies::class, $wp_webfonts ); + } + + protected function get_property_value( $property_name, $class, $wp_webfonts = null ) { + $property = new ReflectionProperty( $class, $property_name ); + $property->setAccessible( true ); + + if ( ! $wp_webfonts ) { + $wp_webfonts = wp_webfonts(); + } + return $property->getValue( $wp_webfonts ); + } + + /** + * Sets up multiple font family and variation mocks. + * + * @param array $inputs Array of array( font-family => variations ) to setup. + * @param WP_Webfonts $wp_webfonts Instance of WP_Webfonts. + * @return stdClass[] Array of registered mocks. + */ + protected function setup_registration_mocks( array $inputs, WP_Webfonts $wp_webfonts ) { + $mocks = array(); + + $build_mock = function( $handle ) use ( &$mocks, $wp_webfonts ) { + $mock = new stdClass(); + $mock->deps = array(); + // Add to each queue. + $mocks[ $handle ] = $mock; + $wp_webfonts->registered[ $handle ] = $mock; + + return $mock; + }; + + foreach ( $inputs as $font_family => $variations ) { + $font_mock = $build_mock( $font_family ); + + foreach ( $variations as $variation_handle => $variation ) { + if ( ! is_string( $variation_handle ) ) { + $variation_handle = $variation; + } + $build_mock( $variation_handle ); + $font_mock->deps[] = $variation_handle; + } + } + + return $mocks; + } + + /** + * Register one or more font-family and its variations to set up a test. + * + * @param string $font_family Font family to test. + * @param array $variations Variations. + * @param WP_Webfonts|null $wp_webfonts Optional. Instance of the WP_Webfonts. + */ + protected function setup_register( $font_family, $variations, $wp_webfonts = null ) { + if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { + $wp_webfonts = wp_webfonts(); + } + + $wp_webfonts->add( $font_family, false ); + + foreach ( $variations as $variation_handle => $variation ) { + if ( ! is_string( $variation_handle ) ) { + $variation_handle = ''; + } + $wp_webfonts->add_variation( $font_family, $variation, $variation_handle ); + } + } +} diff --git a/phpunit/webfonts/wp-webfonts-tests-dataset.php b/phpunit/webfonts/wp-webfonts-tests-dataset.php new file mode 100644 index 00000000000000..33de17c13580ff --- /dev/null +++ b/phpunit/webfonts/wp-webfonts-tests-dataset.php @@ -0,0 +1,513 @@ + array( + 'expected' => 'lato-400-normal', + 'font_family_handle' => 'lato', + 'variation' => array( + 'src' => 'https://example.com/assets/fonts/lato/lato.ttf.woff2', + ), + ), + + 'font-weight and font-style not defined; with variation handle' => array( + 'expected' => 'my-custom-handle', + 'font_family_handle' => 'lato', + 'variation' => array( + 'src' => 'https://example.com/assets/fonts/lato/lato.ttf.woff2', + ), + 'variation_handle' => 'my-custom-handle', + ), + + 'font-weight defined; without variation handle' => array( + 'expected' => 'source-serif-pro-200-900-normal', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'font-family' => 'Source Serif Pro', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + + 'font-weight defined; with variation handle' => array( + 'expected' => 'my_custom_handle', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'font-family' => 'Source Serif Pro', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'variation_handle' => 'my_custom_handle', + ), + + 'font-weight defined; without variation handle' => array( + 'expected' => 'source-serif-pro-200-900-normal', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'font-family' => 'Source Serif Pro', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + + 'font-weight defined; with variation handle' => array( + 'expected' => 'my_custom_handle', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'font-family' => 'Source Serif Pro', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'variation_handle' => 'my_custom_handle', + ), + + 'font-weight defined; without variation handle' => array( + 'expected' => 'source-serif-pro-200-900-normal', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + + 'more variation properties; with variation handle' => array( + 'expected' => 'my-custom-handle', + 'font_family_handle' => 'source-serif-pro', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'variation_handle' => 'my-custom-handle', + ), + ); + } + + /** + * Data provider for testing registration of variations where the font family is not defined. + * + * @return array + */ + public function data_font_family_not_define_in_variation() { + return array( + 'empty string font family handle' => array( + 'font_family_handle' => '', + 'variation' => array( + 'src' => 'https://example.com/assets/fonts/lato/lato.ttf.woff2', + ), + 'expected_message' => 'Font family handle must be a non-empty string', + ), + 'empty string font family handle with font-family defined in variant' => array( + 'font_family_handle' => '', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Lato', + 'font-weight' => '200', + 'src' => 'https://example.com/assets/fonts/lato/lato.ttf.woff2', + ), + 'expected_message' => 'Font family handle must be a non-empty string', + ), + 'non string font family handle' => array( + 'font_family_handle' => 10, + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family handle must be a non-empty string', + ), + ); + } + + /** + * Data provider for testing when the variation's handle can't be determine from the given input. + * + * @return array + */ + public function data_unable_determine_variation_handle() { + return array( + 'integer values' => array( + 'font_family_handle' => 'lato', + 'variation' => array( + 'font-weight' => 400, + 'font-style' => 0, + 'src' => 'https://example.com/assets/fonts/lato.ttf.woff2', + ), + 'expected_message' => 'Variant handle could not be determined as font-weight and/or font-style are require', + ), + 'with empty string font-weight and font-style' => array( + 'font_family_handle' => 'merriweather', + 'variation' => array( + 'font-weight' => '', + 'font-style' => '', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + 'expected_message' => 'Variant handle could not be determined as font-weight and/or font-style are require', + ), + 'integer font-weight, empty string font-style' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-weight' => 400, + 'font-style' => '', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + 'expected_message' => 'Variant handle could not be determined as font-weight and/or font-style are require', + ), + 'empty string font-weight, integer font-style' => array( + 'font_family' => 'lato', + 'variation' => array( + 'font-weight' => '', + 'font-style' => 400, + 'src' => 'https://example.com/assets/fonts/lato.ttf.woff2', + ), + 'expected_message' => 'Variant handle could not be determined as font-weight and/or font-style are require', + ), + ); + } + + /** + * Data provider for testing an invalid variation. + * + * @return array + */ + public function data_invalid_variation() { + return array( + 'src: undefined' => array( + 'expected' => 'Webfont src must be a non-empty string or an array of strings.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Some font', + 'font-weight' => '200', + ), + ), + 'src: null' => array( + 'expected' => 'Webfont src must be a non-empty string or an array of strings.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Some font', + 'font-weight' => '200', + 'src' => null, + ), + ), + 'src: empty string' => array( + 'expected' => 'Webfont src must be a non-empty string or an array of strings.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Some font', + 'font-weight' => '200', + 'src' => '', + ), + ), + 'src: empty array' => array( + 'expected' => 'Webfont src must be a non-empty string or an array of strings.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-weight' => '200', + 'src' => array(), + ), + ), + 'src: array of an empty string' => array( + 'expected' => 'Each webfont src must be a non-empty string.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-weight' => '200', + 'src' => array( '' ), + ), + ), + 'src: array of a non-string' => array( + 'expected' => 'Each webfont src must be a non-empty string.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-weight' => '200', + 'src' => array( null ), + ), + ), + 'src: array with an empty string' => array( + 'expected' => 'Each webfont src must be a non-empty string.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-weight' => '200', + 'src' => array( + 'https://example.com/assets/fonts/merriweather.ttf.woff2', + '', + 'https://example.com/assets/fonts/lato.ttf.woff2', + ), + ), + ), + 'src: array with a non-string' => array( + 'expected' => 'Each webfont src must be a non-empty string.', + 'font_family_handle' => 'lato', + 'variation' => array( + 'provider' => 'local', + 'font-weight' => '200', + 'src' => array( + 'https://example.com/assets/fonts/merriweather.ttf.woff2', + null, + 'https://example.com/assets/fonts/lato.ttf.woff2', + ), + ), + ), + 'provider does not exist' => array( + 'expected' => 'The provider class specified does not exist.', + 'font_family_handle' => 'merriweather', + 'variation' => array( + 'provider' => 'doesnotexit', + 'font-weight' => '200 900', + ), + ), + 'font-weight: null' => array( + 'expected' => 'Webfont font-weight must be a properly formatted string or integer.', + 'font_family_handle' => 'merriweather', + 'variation' => array( + 'font-weight' => null, + 'font-style' => 'normal', + 'src' => 'https://example.com/assets/fonts/lato.ttf.woff2', + ), + ), + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_deregister() { + return array( + 'one family family no variations' => array( + 'font_family' => 'lato', + 'inputs' => array( + 'lato' => array(), + ), + 'handles' => array( 'lato' ), + 'expected' => array(), + ), + 'one family family with 1 variation' => array( + 'font_family' => 'merriweather', + 'inputs' => array( + 'merriweather' => array( + 'merriweather-200-900-normal' => array( + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + ), + ), + 'handles' => array( 'merriweather', 'merriweather-200-900-normal' ), + 'expected' => array(), + ), + 'font family with multiple variations' => array( + 'font_family' => 'Source Serif Pro', + 'inputs' => array( + 'Source Serif Pro' => array( + 'Source Serif Pro-300-normal' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '300', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'Source Serif Pro-900-italic' => array( + 'provider' => 'local', + 'font-style' => 'italic', + 'font-weight' => '900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + ), + 'handles' => array( 'Source Serif Pro', 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ), + 'expected' => array(), + ), + 'multiple font families without variations' => array( + 'font_family' => 'Source Serif Pro', + 'inputs' => array( + 'lato' => array(), + 'my-font' => array(), + 'Source Serif Pro' => array(), + 'some other font' => array(), + ), + 'handles' => array( 'lato', 'my-font', 'Source Serif Pro', 'some other font' ), + 'expected' => array( 'lato', 'my-font', 'some other font' ), + ), + 'multiple font families with variations' => array( + 'font_family' => 'my-font', + 'inputs' => array( + 'merriweather' => array( + 'merriweather-200-900-normal' => array( + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + ), + 'Source Serif Pro' => array( + 'Source Serif Pro-300-normal' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '300', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'Source Serif Pro-900-italic' => array( + 'provider' => 'local', + 'font-style' => 'italic', + 'font-weight' => '900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + 'my-font' => array( + 'my-font-300-italic' => array( + 'font-weight' => '300', + 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', + ), + ), + ), + 'handles' => array( + 'merriweather', + 'merriweather-200-900-normal', + 'Source Serif Pro', + 'Source Serif Pro-300-normal', + 'Source Serif Pro-900-italic', + 'my-font', + 'my-font-300-italic', + ), + 'expected' => array( + 'merriweather', + 'merriweather-200-900-normal', + 'Source Serif Pro', + 'Source Serif Pro-300-normal', + 'Source Serif Pro-900-italic', + ), + ), + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_font_family_handles() { + return array( + 'single word handle' => array( 'lato' ), + 'multiple word handle' => array( 'source-sans-pro' ), + 'single word name' => array( 'Merriweather' ), + 'multiple word name' => array( 'My Cool Font' ), + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_enqueue() { + return array( + '1 font-family' => array( + 'font_family' => 'lato', + 'expected_queue' => array( 'lato' ), + 'expected_queued_before_register' => array( + 'lato' => null, + ), + ), + '2 font families' => array( + 'font_family' => array( 'merriweather', 'my-font' ), + 'expected_queue' => array( 'merriweather', 'my-font' ), + 'expected_queued_before_register' => array( + 'merriweather' => null, + 'my-font' => null, + ), + ), + '3 font families' => array( + 'font_family' => array( 'merriweather', 'my-font', 'Source Serif Pro' ), + 'expected_queue' => array( 'merriweather', 'my-font', 'Source Serif Pro' ), + 'expected_queued_before_register' => array( + 'merriweather' => null, + 'my-font' => null, + 'Source Serif Pro' => null, + ), + ), + ); + } + + protected function get_data_registry() { + return array( + 'lato' => array(), + 'merriweather' => array( + 'merriweather-200-900-normal' => array( + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + ), + 'Source Serif Pro' => array( + 'Source Serif Pro-300-normal' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '300', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'Source Serif Pro-900-italic' => array( + 'provider' => 'local', + 'font-style' => 'italic', + 'font-weight' => '900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + 'my-font' => array( + 'my-font-300-normal' => array( + 'font-weight' => '300', + 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', + ), + 'my-font-300-italic' => array( + 'font-weight' => '300', + 'font-style' => 'italic', + 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', + ), + 'my-font-900-normal' => array( + 'font-weight' => '900', + 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', + ), + ), + ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/add-test.php b/phpunit/webfonts/wp-webfonts/add-test.php new file mode 100644 index 00000000000000..91c79bf1542a6f --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/add-test.php @@ -0,0 +1,45 @@ +assertTrue( $wp_webfonts->add( $handle, false ), 'Registering a handle should return true' ); + $this->assertCount( 1, $wp_webfonts->registered ); + $this->assertArrayHasKey( $handle, $wp_webfonts->registered, 'Font family handle should be in the registry after registration' ); + + } + + /** + * Data provider. + * + * @return array + */ + public function data_handles() { + return array( + 'name: multiple' => array( 'Source Serif Pro' ), + 'handle: multiple' => array( 'source-serif-pro' ), + 'name: single' => array( 'Merriweather' ), + 'handle: single' => array( 'merriweather' ), + 'handle: variation' => array( 'my-custom-font-200-900-normal' ), + ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/addData-test.php b/phpunit/webfonts/wp-webfonts/addData-test.php new file mode 100644 index 00000000000000..d188cf75912278 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/addData-test.php @@ -0,0 +1,20 @@ +add( $font_family_handle, false ); + + $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variation_handle, 'Registering a variation should return its handle' ); + $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' ); + $this->assertSame( array( $expected ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should be registered to font family' ); + } + + /** + * @dataProvider data_valid_variation + * + * @param string|bool $expected Expected results. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. + */ + public function test_add_variation_when_already_registered( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { + $wp_webfonts = new WP_Webfonts(); + $wp_webfonts->add( $font_family_handle, false ); + + // Set up the test. + $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle ); + + // Run the test. + $variant_handle_on_reregister = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variant_handle_on_reregister, 'Variation should be registered to font family' ); + $this->assertSame( $variation_handle, $variant_handle_on_reregister, 'Variation should return the previously registered variant handle' ); + $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should only be registered once' ); + + $this->assertCount( 2, $wp_webfonts->registered ); + $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' ); + } + + /** + * @dataProvider data_valid_variation + * + * @param string|bool $expected Expected results. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. + */ + public function test_add_variation_registers_font_family( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { + $wp_webfonts = new WP_Webfonts(); + + $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variation_handle, 'Variation should return its registered handle' ); + + // Extra checks to ensure both are registered. + $this->assertCount( 2, $wp_webfonts->registered ); + $this->assertArrayHasKey( $font_family_handle, $wp_webfonts->registered, 'Font family handle should be in the registry after registration' ); + $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' ); + $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should be registered to the font family' ); + } + + /** + * @dataProvider data_font_family_not_define_in_variation + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + */ + public function test_add_variation_should_not_register_font_family_or_variant( $font_family_handle, array $variation ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Font family handle must be a non-empty string.' ); + + $wp_webfonts = new WP_Webfonts(); + $wp_webfonts->add_variation( $font_family_handle, $variation ); + + $this->assertEmpty( $wp_webfonts->registered, 'Registered queue should be empty' ); + $this->assertEmpty( $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should not be registered to the font family' ); + } + + /** + * @dataProvider data_font_family_not_define_in_variation + * @dataProvider data_unable_determine_variation_handle + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $expected_message Expected notice message. + */ + public function test_add_variation_should_not_register_variation_when_font_family_not_defined( $font_family_handle, array $variation, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $wp_webfonts = new WP_Webfonts(); + $this->assertNull( $wp_webfonts->add_variation( $font_family_handle, $variation ) ); + } + + /** + * @dataProvider data_unable_determine_variation_handle + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + */ + public function test_add_variation_should_register_font_family_when_variant_fails_to_register( $font_family_handle, array $variation ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' ); + + $wp_webfonts = new WP_Webfonts(); + $wp_webfonts->add_variation( $font_family_handle, $variation ); + + $this->assertCount( 1, $wp_webfonts->registered ); + $this->assertArrayHasKey( $font_family_handle, $wp_webfonts->registered ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/allDeps-test.php b/phpunit/webfonts/wp-webfonts/allDeps-test.php new file mode 100644 index 00000000000000..7babaf9f7e8997 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/allDeps-test.php @@ -0,0 +1,20 @@ +enqueue( $font_family ); + + $this->assertSame( $expected, $this->get_queued_before_register( $wp_webfonts ), 'Font family(ies) should be added to before registered queue' ); + $this->assertEmpty( $this->queue, 'Font family(ies) should not be added to the enqueue queue when not registered' ); + } + + /** + * Integration test for enqueuing a font family and all of its variations. + * + * @dataProvider data_enqueue + * + * @param string|string[] $font_family Font family to test. + * @param array $expected Expected queue. + */ + public function test_after_registration( $font_family, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + foreach ( $this->get_data_registry() as $handle => $variations ) { + $this->setup_register( $handle, $variations, $wp_webfonts ); + } + + $wp_webfonts->enqueue( $font_family ); + + $this->assertEmpty( $this->get_queued_before_register( $wp_webfonts ), '"queued_before_register" queue should be empty' ); + $this->assertSame( $expected, $wp_webfonts->queue, 'Queue should contain the given font family(ies)' ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/getData-test.php b/phpunit/webfonts/wp-webfonts/getData-test.php new file mode 100644 index 00000000000000..ff0922063d6895 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/getData-test.php @@ -0,0 +1,20 @@ +assertEmpty( $wp_webfonts->get_enqueued() ); + } + + /** + * Unit test for when font families are enqueued. + * + * @dataProvider data_enqueue + * + * @param string|string[] $not_used Not used. + * @param array $expected Expected queue. + */ + public function test_when_font_families_are_enqueued( $not_used, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + $wp_webfonts->queue = $expected; + + $this->assertSame( $expected, $wp_webfonts->get_enqueued() ); + } + + /** + * Full integration test that registers and enqueues the queue + * is properly wired for "get_enqueued()". + * + * @dataProvider data_enqueue + * + * @param string|string[] $font_family Font family to test. + * @param array $expected Expected queue. + */ + public function test_integration( $font_family, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + + // Register and enqueue. + foreach ( $this->get_data_registry() as $handle => $variations ) { + $this->setup_register( $handle, $variations, $wp_webfonts ); + } + $wp_webfonts->enqueue( $font_family ); + + $this->assertSame( $expected, $wp_webfonts->get_enqueued() ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/getProviders-test.php b/phpunit/webfonts/wp-webfonts/getProviders-test.php new file mode 100644 index 00000000000000..db33e34e276b31 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/getProviders-test.php @@ -0,0 +1,100 @@ +wp_webfonts = new WP_Webfonts(); + + $this->providers_property = new ReflectionProperty( WP_Webfonts::class, 'providers' ); + $this->providers_property->setAccessible( true ); + } + + public function test_should_be_empty() { + $actual = $this->wp_webfonts->get_providers(); + $this->assertIsArray( $actual, 'Should return an empty array' ); + $this->assertEmpty( $actual, 'Should return an empty array when no providers are registered' ); + } + + /** + * @dataProvider data_get_providers + * + * @param array $providers Array of providers to test. + * @param array $expected Expected results. + */ + public function test_get_providers( array $providers, array $expected ) { + $this->setup_providers( $providers ); + $this->assertSame( $expected, $this->wp_webfonts->get_providers() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_providers() { + $local = array( + 'local' => array( + 'class' => WP_Webfonts_Provider_Local::class, + 'fonts' => array(), + ), + ); + $mock = array( + 'mock' => array( + 'class' => Mock_Provider::class, + 'fonts' => array(), + ), + ); + + return array( + 'local provider' => array( + 'providers' => array( + 'local' => WP_Webfonts_Provider_Local::class, + ), + 'expected' => $local, + ), + 'mock provider' => array( + 'providers' => array( + 'mock' => Mock_Provider::class, + ), + 'expected' => $mock, + ), + 'local and mock providers' => array( + 'providers' => array( + 'local' => WP_Webfonts_Provider_Local::class, + 'mock' => Mock_Provider::class, + ), + 'expected' => array_merge( $local, $mock ), + ), + ); + } + + private function setup_providers( array $providers ) { + $data = array(); + + foreach ( $providers as $provider_id => $class ) { + $data[ $provider_id ] = array( + 'class' => $class, + 'fonts' => array(), + ); + } + + $this->providers_property->setValue( $this->wp_webfonts, $data ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/getRegistered-test.php b/phpunit/webfonts/wp-webfonts/getRegistered-test.php new file mode 100644 index 00000000000000..69e28e7885cd8b --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/getRegistered-test.php @@ -0,0 +1,65 @@ +assertEmpty( $wp_webfonts->get_registered() ); + } + + /** + * @dataProvider data_get_registered + */ + public function test_get_registered( $inputs ) { + $wp_webfonts = new WP_Webfonts(); + $mocks = $this->setup_registration_mocks( $inputs, $wp_webfonts ); + $expected = array_keys( $mocks ); + + $this->assertSame( $expected, $wp_webfonts->get_registered() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_registered() { + return array( + 'no variations' => array( + 'inputs' => array( + 'lato' => array(), + ), + ), + 'with 1 variation' => array( + 'inputs' => array( + 'Source Serif Pro' => array( 'variation-1' ), + ), + ), + 'with 2 variations' => array( + 'inputs' => array( + 'my-cool-font' => array( 'cool-1', 'cool-2' ), + ), + ), + 'when multiple font families registered' => array( + 'inputs' => array( + 'font-family-1' => array( 'variation-11', 'variation-12' ), + 'font-family-2' => array( 'variation-21', 'variation-22' ), + 'font-family-3' => array( 'variation-31', 'variation-32' ), + ), + ), + ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/getVariations-test.php b/phpunit/webfonts/wp-webfonts/getVariations-test.php new file mode 100644 index 00000000000000..f73b51b369c767 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/getVariations-test.php @@ -0,0 +1,108 @@ +setup_registration_mocks( $inputs, $wp_webfonts ); + + $actual = $wp_webfonts->get_variations( $handle ); + $this->assertIsArray( $actual, 'WP_Webfonts::get_variations() should return an array' ); + $this->assertEmpty( $actual, 'WP_Webfonts::get_variations() should return an empty array' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_variations_when_font_family_not_registered() { + return array( + 'Integer font family' => array( 0 ), + 'Font family not registered' => array( 'font-not-registered' ), + 'Other font families registered' => array( + 'handle' => 'font-family-4', + 'inputs' => array( + 'font-family-1' => array( 'variation-1', 'variation-2' ), + 'font-family-2' => array( 'variation-21', 'variation-22' ), + 'font-family-3' => array( 'variation-31', 'variation-32' ), + ), + ), + ); + } + + /** + * @dataProvider data_get_variations + * + * @param string $font_family The font family to test. + * @param array $inputs Array of array( font-family => variations ). + * @param array $expected Expected result. + */ + public function test_get_variations( $font_family, array $inputs, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + $this->setup_registration_mocks( $inputs, $wp_webfonts ); + + $actual = $wp_webfonts->get_variations( $font_family ); + $this->assertIsArray( $actual, 'WP_Webfonts::get_variations() should return an array' ); + $this->assertSame( $expected, $actual, 'WP_Webfonts::get_variations() should return the expected array of variations' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_variations() { + return array( + 'no variations' => array( + 'font_family' => 'lato', + 'inputs' => array( + 'lato' => array(), + ), + 'expected' => array(), + ), + 'with 1 variation' => array( + 'font_family' => 'Source Serif Pro', + 'inputs' => array( + 'Source Serif Pro' => array( 'variation-1' ), + ), + 'expected' => array( 'variation-1' ), + ), + 'with 2 variations' => array( + 'font_family' => 'my-cool-font', + 'inputs' => array( + 'my-cool-font' => array( 'cool-1', 'cool-2' ), + ), + 'expected' => array( 'cool-1', 'cool-2' ), + ), + 'when multiple font families registered' => array( + 'font_family' => 'font-family-2', + 'inputs' => array( + 'font-family-1' => array( 'variation-1', 'variation-2' ), + 'font-family-2' => array( 'variation-21', 'variation-22' ), + 'font-family-3' => array( 'variation-1', 'variation-2' ), + ), + 'expected' => array( 'variation-21', 'variation-22' ), + ), + ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/query-test.php b/phpunit/webfonts/wp-webfonts/query-test.php new file mode 100644 index 00000000000000..b0eb23debe8be1 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/query-test.php @@ -0,0 +1,20 @@ +setup_registration_mocks( $inputs, $wp_webfonts ); + // Test the before state, just to make sure. + $this->assertArrayHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should contain the font family before remove' ); + $this->assertSame( $registered_handles, array_keys( $wp_webfonts->registered ), 'Font family and variations should be registered before remove' ); + + $wp_webfonts->remove_font_family( $font_family ); + + $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' ); + $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' ); + } + + /** + * @dataProvider data_deregister + * + * @param string $font_family Font family to test. + * @param array $inputs Font family(ies) and variations to pre-register. + * @param array $registered_handles Expected handles after registering. + * @param array $expected Array of expected handles. + */ + public function test_when_not_registered( $font_family, array $inputs, array $registered_handles, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + unset( $inputs[ $font_family ] ); + $this->setup_registration_mocks( $inputs, $wp_webfonts ); + + $wp_webfonts->remove_font_family( $font_family ); + + $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' ); + $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' ); + } + + /** + * Full integration test for removing a font family and all of its variation when font family is registered. + * + * @dataProvider data_deregister + * + * @param string $font_family Font family to test. + * @param array $inputs Font family(ies) and variations to pre-register. + * @param array $registered_handles Expected handles after registering. + * @param array $expected Array of expected handles. + */ + public function test_integration_when_registered( $font_family, array $inputs, array $registered_handles, array $expected ) { + $wp_webfonts = new WP_Webfonts(); + // Register all font families and their variations. + foreach ( $inputs as $handle => $variations ) { + $wp_webfonts->add( $handle, false ); + foreach ( $variations as $variation_handle => $variation ) { + if ( ! is_string( $variation_handle ) ) { + $variation_handle = ''; + } + $wp_webfonts->add_variation( $handle, $variation, $variation_handle ); + } + } + // Test the before state, just to make sure. + $this->assertArrayHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should contain the font family before remove' ); + $this->assertSame( $registered_handles, array_keys( $wp_webfonts->registered ), 'Font family and variations should be registered before remove' ); + + $wp_webfonts->remove_font_family( $font_family ); + + $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' ); + $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' ); + } +} diff --git a/phpunit/webfonts/wp-webfonts/removeVariation-test.php b/phpunit/webfonts/wp-webfonts/removeVariation-test.php new file mode 100644 index 00000000000000..f774dedb74e7f6 --- /dev/null +++ b/phpunit/webfonts/wp-webfonts/removeVariation-test.php @@ -0,0 +1,20 @@ +set_up_mock( 'remove_font_family' ); + $mock->expects( $this->once() ) + ->method( 'remove_font_family' ) + ->with( + $this->identicalTo( $font_family_handle ) + ); + + wp_deregister_font_family( $font_family_handle ); + } + + /** + * Integration test for enqueuing before registering a font family and all of its variations. + * + * @dataProvider data_font_family_handles + * + * @param string $font_family Font family to test. + */ + public function test_deregister_before_registration( $font_family ) { + wp_deregister_font_family( $font_family ); + + $this->assertIsArray( $this->get_registered(), 'Registration queue should be an array' ); + $this->assertEmpty( $this->get_registered(), 'Registration queue should be empty after deregistering' ); + } + + /** + * Integration test for deregistering a font family and all of its variations. + * + * @dataProvider data_deregister + * + * @param string $font_family Font family to test. + * @param array $inputs Font family(ies) and variations to pre-register. + * @param array $registered_handles Expected handles after registering. + * @param array $expected Array of expected handles. + */ + public function test_deregister_after_registration( $font_family, array $inputs, array $registered_handles, array $expected ) { + foreach ( $inputs as $handle => $variations ) { + $this->setup_register( $handle, $variations ); + } + // Test the before state, just to make sure. + $this->assertSame( $registered_handles, $this->get_registered_handles(), 'Font family and variations should be registered before deregistering' ); + + wp_deregister_font_family( $font_family ); + + // Test after deregistering. + $this->assertIsArray( $this->get_registered_handles(), 'Registration queue should be an array' ); + $this->assertSame( $expected, $this->get_registered_handles(), 'Registration queue should match after deregistering' ); + } +} diff --git a/phpunit/webfonts/wpEnqueueWebfont-test.php b/phpunit/webfonts/wpEnqueueWebfont-test.php new file mode 100644 index 00000000000000..323fe51c5e8a68 --- /dev/null +++ b/phpunit/webfonts/wpEnqueueWebfont-test.php @@ -0,0 +1,69 @@ +set_up_mock( 'enqueue' ); + $mock->expects( $this->once() ) + ->method( 'enqueue' ) + ->with( + $this->identicalTo( $font_family ) + ); + + wp_enqueue_webfont( $font_family ); + } + + /** + * Integration test for enqueuing a font family and all of its variations. + * + * @dataProvider data_enqueue + * + * @param string|string[] $font_family Font family to test. + * @param array $expected Expected queue. + */ + public function test_enqueue_after_registration( $font_family, array $expected ) { + foreach ( $this->get_data_registry() as $handle => $variations ) { + $this->setup_register( $handle, $variations ); + } + + wp_enqueue_webfont( $font_family ); + $this->assertEmpty( $this->get_queued_before_register(), '"queued_before_register" queue should be empty' ); + $this->assertSame( $expected, $this->get_enqueued_handles(), 'Queue should contain the given font family(ies)' ); + } + + /** + * Integration test for enqueuing before registering a font family and all of its variations. + * + * @dataProvider data_enqueue + * + * @param string|string[] $font_family Font family to test. + * @param array $not_used Not used. + * @param array $expected Expected "queued_before_register" queue. + */ + public function test_enqueue_before_registration( $font_family, array $not_used, array $expected ) { + wp_enqueue_webfont( $font_family ); + + $this->assertSame( $expected, $this->get_queued_before_register(), '"queued_before_register" queue should contain the given font family(ies)' ); + $this->assertEmpty( $this->get_enqueued_handles(), 'Queue should be empty' ); + } +} diff --git a/phpunit/webfonts/wpRegisterFontFamily-test.php b/phpunit/webfonts/wpRegisterFontFamily-test.php new file mode 100644 index 00000000000000..6064fbac37c761 --- /dev/null +++ b/phpunit/webfonts/wpRegisterFontFamily-test.php @@ -0,0 +1,133 @@ +set_up_mock( 'add' ); + $mock->expects( $this->once() ) + ->method( 'add' ) + ->with( + $this->identicalTo( $expected_handle ), + $this->identicalTo( false ) + ) + ->will( $this->returnValue( $expected_handle ) ); + + $this->assertSame( $expected_handle, wp_register_font_family( $font_family ), 'Font family should return true after registering' ); + } + + /** + * Integration test for registering a font family. + * + * @dataProvider data_font_family + * + * @covers WP_Webfonts::add + * + * @param string $font_family Font family to test. + * @param string $expected_handle Expected registered handle. + */ + public function test_register( $font_family, $expected_handle ) { + $this->assertSame( $expected_handle, wp_register_font_family( $font_family ), 'Font family should return true after registering' ); + $this->assertSame( array( $expected_handle ), $this->get_registered_handles(), 'After registering, the registry should contain the font family handle' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_font_family() { + return array( + 'single word name' => array( + 'font_family' => 'Lato', + 'expected_handle' => 'lato', + ), + 'multiple word name' => array( + 'font_family' => 'Source Sans Pro', + 'expected_handle' => 'source-sans-pro', + ), + 'handle' => array( + 'font_family' => 'source-serif-pro', + 'expected_handle' => 'source-serif-pro', + ), + ); + } + + /** + * Unit test for registering a font-family that mocks WP_Webfonts. + * + * @dataProvider data_invalid_font_family + * + * @param string $invalid_input Invalid input to test. + */ + public function test_unit_register_fails( $invalid_input ) { + $mock = $this->set_up_mock( 'add' ); + $mock->expects( $this->never() )->method( 'add' ); + + $this->assertNull( wp_register_font_family( $invalid_input ), 'Registering an invalid input should return null' ); + } + + /** + * Integration test for registering a font family. + * + * @dataProvider data_invalid_font_family + * + * @covers WP_Webfonts::add + * + * @param string $invalid_input Invalid input to test. + */ + public function test_register_fails( $invalid_input ) { + $this->assertNull( wp_register_font_family( $invalid_input ), 'Registering an invalid input should return null' ); + $this->assertEmpty( $this->get_registered(), 'Invalid input should not register' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_invalid_font_family() { + return array( + 'non-string' => array( null ), + 'empty string' => array( '' ), + ); + } + + /** + * Integration test for attempting to re-register a font family. + * + * @dataProvider data_font_family + * + * @covers WP_Webfonts::add + * + * @param string $font_family Font family to test. + * @param string $expected_handle Expected registered handle. + */ + public function test_re_register( $font_family, $expected_handle ) { + // Register the font family. + wp_register_font_family( $font_family ); + + // Attempt to re-register it. + $this->assertNull( wp_register_font_family( $font_family ), 'Font family should return true after registering' ); + $this->assertSame( array( $expected_handle ), $this->get_registered_handles(), 'The font family should only be registered once' ); + } +} diff --git a/phpunit/webfonts/wpRegisterWebfont-test.php b/phpunit/webfonts/wpRegisterWebfont-test.php new file mode 100644 index 00000000000000..c6445813ef1403 --- /dev/null +++ b/phpunit/webfonts/wpRegisterWebfont-test.php @@ -0,0 +1,281 @@ +add( $handle, false ); + } + + /** + * Unit test for registering a variation with a variation handle given. + * + * @dataProvider data_register + * + * @param string $font_family_handle Font family for this variation. + * @param array $variation Web font to test. + * @param array $expected Expected results. + */ + public function test_register_with_mock_when_handle_given( $font_family_handle, array $variation, $expected ) { + $mock = $this->set_up_mock( 'add_variation' ); + $variation_handle = $expected[1]; + + $mock->expects( $this->once() ) + ->method( 'add_variation' ) + ->with( + $this->identicalTo( $font_family_handle ), + $this->identicalTo( $variation ), + $this->identicalTo( $variation_handle ) + ) + ->will( $this->returnValue( $font_family_handle ) ); + + $this->assertSame( $font_family_handle, wp_register_webfont( $variation, $font_family_handle, $variation_handle ) ); + } + + /** + * Integration test for registering a variation when the font family was previously registered. + * + * @dataProvider data_register + * + * @covers WP_Webfonts::add_variation + * + * @param string $font_family_handle Font family for this variation. + * @param array $variation Web font to test. + * @param array $expected Expected results. + */ + public function test_register( $font_family_handle, array $variation, $expected ) { + $this->setup_font_family( $font_family_handle ); + + $this->assertSame( $font_family_handle, wp_register_webfont( $variation, $font_family_handle ), 'Registering should return the font family handle' ); + $this->assertSame( $expected, $this->get_registered_handles(), 'Registry should contain the font family and variant handles' ); + } + + /** + * Integration test for testing the font family is registered during the variation registration process. + * + * @dataProvider data_register_with_deprecated_structure + * + * @covers WP_Webfonts::add + * @covers WP_Webfonts::add_variation + * + * @param array $variation Web font to test. + * @param array $expected Expected results. + */ + public function test_registers_font_family( array $variation, $expected ) { + $font_family_handle = $expected[0]; + + wp_register_webfont( $variation ); + $this->assertContains( $font_family_handle, $this->get_registered_handles() ); + } + + /** + * Integration test to ensure registration does not automatically enqueue. + * + * @dataProvider data_register + * + * @covers WP_Webfonts::add_variation + * + * @param string $font_family_handle Font family handle. + * @param array $variation Variation. + */ + public function test_does_not_enqueue_when_registering( $font_family_handle, array $variation ) { + $this->setup_font_family( $font_family_handle ); + wp_register_webfont( $variation, $font_family_handle ); + + $this->assertEmpty( $this->get_enqueued_handles() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_register() { + return array( + 'Source Serif Pro' => array( + 'font_family' => 'source-serif-pro', + 'variation' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected' => array( + 'source-serif-pro', + 'source-serif-pro-200-900-normal', + ), + ), + 'Merriweather' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-style' => 'italic', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + ), + 'expected' => array( + 'merriweather', + 'merriweather-400-italic', + ), + ), + ); + } + + /** + * @dataProvider data_register_with_deprecated_structure + * + * @covers WP_Webfonts::add_variation + * + * @param array $variation Web font to test. + * @param array $expected Expected results. + */ + public function test_registers_with_deprecated_structure( + array $variation, $expected + ) { + $font_family_handle = $expected[0]; + $this->setup_font_family( $font_family_handle ); + + $this->assertSame( $font_family_handle, wp_register_webfont( $variation ), 'Registering should return the registered font family handle' ); + $this->assertSame( $expected, $this->get_registered_handles(), 'Registry should contain the font family and variant handles' ); + } + + /** + * @dataProvider data_register_with_deprecated_structure + * + * @covers WP_Webfonts::add_variation + * + * @param array $variation Web font to test. + * @param array $expected Expected results. + */ + public function test_registers_font_family_with_deprecated_structure( + array $variation, $expected + ) { + $font_family_handle = $expected[0]; + + wp_register_webfont( $variation ); + $this->assertContains( $font_family_handle, $this->get_registered_handles() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_register_with_deprecated_structure() { + return array( + 'font-family as name' => array( + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-weight' => '200 900', + 'src' => 'https://example.com/assets/fonts/font.ttf.woff2', + ), + 'expected' => array( + 'source-serif-pro', + 'source-serif-pro-200-900-normal', + ), + ), + 'font-family as handle/slug' => array( + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'my-font-family', + 'font-style' => 'italic', + 'src' => 'https://example.com/assets/fonts/font.ttf.woff2', + ), + 'expected' => array( + 'my-font-family', + 'my-font-family-400-italic', + ), + ), + ); + } + + /** + * Integration test for testing when font family registration fails. + * + * @dataProvider data_invalid_font_family + * + * @param string $font_family_handle Font family for this variation. + * @param array $variation Web font to test. + * @param string $expected_meessage Expected notice message. + */ + public function test_font_family_registration_fails( $font_family_handle, array $variation, $expected_meessage ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_meessage ); + + $this->assertNull( wp_register_webfont( $variation, $font_family_handle ), 'Should return null when invalid font family given' ); + $this->assertEmpty( $this->get_registered_handles(), 'Font family and variation should not be registered' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_invalid_font_family() { + return array( + 'non-string' => array( + 'font_family_handle' => null, + 'variation' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not found.', + 'font-family' => null, + ), + 'empty string' => array( + 'font_family_handle' => '', + 'variation' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not found.', + ), + 'non-string in deprecated structure' => array( + 'font_family_handle' => '', + 'variation' => array( + 'provider' => 'local', + 'font-family' => null, + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not defined in the variation.', + ), + 'empty string in deprecated structure' => array( + 'font_family_handle' => '', + 'variation' => array( + 'provider' => 'local', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not defined in the variation.', + ), + ); + } +} diff --git a/phpunit/webfonts/wpRegisterWebfontVariation-test.php b/phpunit/webfonts/wpRegisterWebfontVariation-test.php new file mode 100644 index 00000000000000..d2445f86fe5f42 --- /dev/null +++ b/phpunit/webfonts/wpRegisterWebfontVariation-test.php @@ -0,0 +1,133 @@ +add( $font_family_handle, false ); + + $variation_handle = wp_register_webfont_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variation_handle, 'Variation should return handle' ); + $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle ), 'Variation should be registered to font family' ); + } + + /** + * @dataProvider data_valid_variation + * + * @param string|bool $expected Expected results. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. + */ + public function test_variation_already_registered( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { + wp_webfonts()->add( $font_family_handle, false ); + + // Set up the test. + $variation_handle = wp_register_webfont_variation( $font_family_handle, $variation, $variation_handle ); + + // Run the test. + $variant_handle_on_reregister = wp_register_webfont_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variant_handle_on_reregister, 'Variation should be registered to font family' ); + $this->assertSame( $variation_handle, $variant_handle_on_reregister, 'Variation should return the previously registered variant handle' ); + $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle ), 'Variation should only be registered once' ); + $expected = array( $font_family_handle, $variation_handle ); + $this->assertSameSets( $expected, $this->get_registered_handles(), 'Register queue should contain the font family and its one variant' ); + } + + /** + * @dataProvider data_valid_variation + * + * @param string|bool $expected Expected results. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $variation_handle Optional. The variation's handle. + */ + public function test_register_font_family_and_variation( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { + $variation_handle = wp_register_webfont_variation( $font_family_handle, $variation, $variation_handle ); + $this->assertSame( $expected, $variation_handle, 'Variation should return its registered handle' ); + + // Extra checks to ensure both are registered. + $expected = array( $font_family_handle, $variation_handle ); + $this->assertSame( $expected, wp_webfonts()->get_registered(), 'Font family and variation should be registered' ); + $this->assertSame( array( $variation_handle ), wp_webfonts()->get_variations( $font_family_handle ), 'Variation should be registered to the font family' ); + } + + /** + * @dataProvider data_font_family_not_define_in_variation + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + */ + public function test_should_not_register_font_family_or_variant( $font_family_handle, array $variation ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Font family handle must be a non-empty string.' ); + + wp_register_webfont_variation( $font_family_handle, $variation ); + $this->assertEmpty( wp_webfonts()->get_registered(), 'Registered queue should be empty' ); + $this->assertEmpty( wp_webfonts()->get_variations( $font_family_handle ), 'Variation should not be registered to the font family' ); + } + + /** + * @dataProvider data_font_family_not_define_in_variation + * @dataProvider data_unable_determine_variation_handle + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + * @param string $expected_message Expected notice message. + */ + public function test_should_not_register_variation_when_font_family_not_defined( $font_family_handle, array $variation, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $this->assertNull( wp_register_webfont_variation( $font_family_handle, $variation ) ); + } + + /** + * @dataProvider data_unable_determine_variation_handle + * + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + */ + public function test_should_register_font_family_when_variant_fails_to_register( $font_family_handle, array $variation ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' ); + + wp_register_webfont_variation( $font_family_handle, $variation ); + $expected = $font_family_handle; + $this->assertContains( $expected, wp_webfonts()->get_registered() ); + } + + /** + * @dataProvider data_invalid_variation + * + * @param string $expected_message Expected notice message. + * @param string $font_family_handle The font family's handle for this variation. + * @param array $variation An array of variation properties to add. + */ + public function test_should_not_register_when_variation_fails_validation( $expected_message, $font_family_handle, array $variation ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $this->assertNull( wp_register_webfont_variation( $font_family_handle, $variation ) ); + } +} diff --git a/phpunit/webfonts/wpRegisterWebfonts-test.php b/phpunit/webfonts/wpRegisterWebfonts-test.php new file mode 100644 index 00000000000000..5a85e746b09217 --- /dev/null +++ b/phpunit/webfonts/wpRegisterWebfonts-test.php @@ -0,0 +1,147 @@ +assertSame( $expected['wp_register_webfonts'], wp_register_webfonts( $webfonts ), 'Font family handle(s) should be returned' ); + $this->assertSame( $expected['get_registered'], $this->get_registered_handles(), 'Web fonts should match registered queue' ); + } + + /** + * @dataProvider data_deprecated_structure + * + * @expectedIncorrectUsage wp_register_webfonts + * + * @param array $webfonts Array of webfonts to test. + * @param array $expected Expected results. + */ + public function test_register_with_deprecated_structure( array $webfonts, array $expected ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected['message'] ); + + $this->assertSame( $expected['wp_register_webfonts'], wp_register_webfonts( $webfonts ), 'Font family handle(s) should be returned' ); + $this->assertSame( $expected['get_registered'], $this->get_registered_handles(), 'Web fonts should match registered queue' ); + } + + /** + * @dataProvider data_webfonts + * + * @param array $webfonts Array of webfonts to test. + */ + public function test_web_fonts_do_not_enqueue_on_registration( array $webfonts ) { + wp_register_webfonts( $webfonts ); + $this->assertEmpty( $this->get_enqueued_handles() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_webfonts() { + return array( + 'font family keyed with slug' => array( + 'webfonts' => array( + 'source-serif-pro' => array( + array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + ), + 'expected' => array( + 'wp_register_webfonts' => array( 'source-serif-pro' ), + 'get_registered' => array( + 'source-serif-pro', + 'source-serif-pro-200-900-normal', + ), + ), + ), + 'font family keyed with name' => array( + 'webfonts' => array( + 'Source Serif Pro' => array( + array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + array( + 'provider' => 'local', + 'font-style' => 'italic', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + ), + 'expected' => array( + 'wp_register_webfonts' => array( 'source-serif-pro' ), + 'get_registered' => array( + 'source-serif-pro', + 'source-serif-pro-200-900-normal', + 'source-serif-pro-200-900-italic', + ), + ), + ), + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_deprecated_structure() { + return array( + 'font family not keyed' => array( + 'webfonts' => array( + array( + array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + ), + 'expected' => array( + 'wp_register_webfonts' => array(), + 'get_registered' => array(), + 'message' => 'Font family not found.', + ), + ), + ); + } +} diff --git a/phpunit/webfonts/wpWebfonts-test.php b/phpunit/webfonts/wpWebfonts-test.php new file mode 100644 index 00000000000000..f12fa060a0f4ef --- /dev/null +++ b/phpunit/webfonts/wpWebfonts-test.php @@ -0,0 +1,38 @@ +assertInstanceOf( WP_Webfonts::class, wp_webfonts() ); + } + + public function test_global_set() { + global $wp_webfonts; + $this->assertNull( $wp_webfonts ); + $instance = wp_webfonts(); + $this->assertInstanceOf( WP_Webfonts::class, $wp_webfonts ); + $this->assertSame( $instance, $wp_webfonts ); + } + + public function test_local_provider_is_automatically_registered() { + $expected = array( + 'local' => array( + 'class' => 'WP_Webfonts_Provider_Local', + 'fonts' => array(), + ), + ); + $this->assertSame( $expected, wp_webfonts()->get_providers() ); + } +} diff --git a/phpunit/class-wp-webfonts-local-provider-test.php b/phpunit/webfonts/wpWebfontsProviderLocal-test.php similarity index 95% rename from phpunit/class-wp-webfonts-local-provider-test.php rename to phpunit/webfonts/wpWebfontsProviderLocal-test.php index c9ab3e2cd8b958..8a87c03a8e3ebd 100644 --- a/phpunit/class-wp-webfonts-local-provider-test.php +++ b/phpunit/webfonts/wpWebfontsProviderLocal-test.php @@ -2,9 +2,9 @@ /** * @group webfonts - * @covers WP_WebfontsLocal_Provider + * @covers WP_Webfonts_Provider_Local */ -class WP_Webfonts_Provider_Local_Test extends WP_UnitTestCase { +class Tests_Webfonts_WpWebfontsProviderLocal extends WP_UnitTestCase { private $provider; private $theme_root; private $orig_theme_dir; @@ -17,27 +17,6 @@ public function set_up() { $this->set_up_theme(); } - /** - * Local `src` paths to need to be relative to the theme. This method sets up the - * `wp-content/themes/` directory to ensure consistency when running tests. - */ - private function set_up_theme() { - $this->theme_root = realpath( DIR_TESTDATA . '/themedir1' ); - $this->orig_theme_dir = $GLOBALS['wp_theme_directories']; - $GLOBALS['wp_theme_directories'] = array( $this->theme_root ); - - $theme_root_callback = function () { - return $this->theme_root; - }; - add_filter( 'theme_root', $theme_root_callback ); - add_filter( 'stylesheet_root', $theme_root_callback ); - add_filter( 'template_root', $theme_root_callback ); - - // Clear caches. - wp_clean_themes_cache(); - unset( $GLOBALS['wp_themes'] ); - } - function tear_down() { // Restore the original theme directory setup. $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir; @@ -109,7 +88,10 @@ public function data_get_css() { ), ), 'expected' => << @font-face{font-family:"Open Sans";font-style:italic;font-weight:bold;src:local("Open Sans"), url('http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf') format('truetype');} + + CSS , ), @@ -133,13 +115,37 @@ public function data_get_css() { ), ), 'expected' => << @font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-stretch:normal;src:local("Source Serif Pro"), url('http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-stretch:normal;src:local("Source Serif Pro"), url('http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');} + + CSS , ), ); } + /** + * Local `src` paths to need to be relative to the theme. This method sets up the + * `wp-content/themes/` directory to ensure consistency when running tests. + */ + private function set_up_theme() { + $this->theme_root = realpath( DIR_TESTDATA . '/themedir1' ); + $this->orig_theme_dir = $GLOBALS['wp_theme_directories']; + $GLOBALS['wp_theme_directories'] = array( $this->theme_root ); + + $theme_root_callback = function () { + return $this->theme_root; + }; + add_filter( 'theme_root', $theme_root_callback ); + add_filter( 'stylesheet_root', $theme_root_callback ); + add_filter( 'template_root', $theme_root_callback ); + + // Clear caches. + wp_clean_themes_cache(); + unset( $GLOBALS['wp_themes'] ); + } + private function get_webfonts_property() { $property = new ReflectionProperty( $this->provider, 'webfonts' ); $property->setAccessible( true ); diff --git a/phpunit/webfonts/wpWebfontsUtils-test.php b/phpunit/webfonts/wpWebfontsUtils-test.php new file mode 100644 index 00000000000000..ce626b9bb60f89 --- /dev/null +++ b/phpunit/webfonts/wpWebfontsUtils-test.php @@ -0,0 +1,366 @@ +assertSame( $expected, WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_convert_font_family_into_handle() { + return array( + 'font family single word name' => array( + 'font_family' => 'Merriweather', + 'expected' => 'merriweather', + ), + 'font family multiword name' => array( + 'font_family' => 'Source Sans Pro', + 'expected' => 'source-sans-pro', + ), + 'font family handle delimited by hyphens' => array( + 'font_family' => 'source-serif-pro', + 'expected' => 'source-serif-pro', + ), + 'font family handle delimited by underscore' => array( + 'font_family' => 'source_serif_pro', + 'expected' => 'source_serif_pro', + ), + 'font family handle delimited by hyphens and underscore' => array( + 'font_family' => 'my-custom_font_family', + 'expected' => 'my-custom_font_family', + ), + 'font family handle delimited mixture' => array( + 'font_family' => 'My custom_font-family', + 'expected' => 'my-custom_font-family', + ), + ); + } + + /** + * @dataProvider data_convert_font_family_into_handle_with_invalid_input + * + * @covers WP_Webfonts_Utils::convert_font_family_into_handle + * + * @param mixed $invalid_input Invalid input. + */ + public function test_convert_font_family_into_handle_with_invalid_input( $invalid_input ) { + $this->assertNull( WP_Webfonts_Utils::convert_font_family_into_handle( $invalid_input ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_convert_font_family_into_handle_with_invalid_input() { + return array( + 'empty string' => array( '' ), + 'integer' => array( 10 ), + 'font family wrapped in an array' => array( array( 'source-serif-pro' ) ), + ); + } + + /** + * @dataProvider data_convert_variation_into_handle + * + * @covers WP_Webfonts_Utils::convert_variation_into_handle + * + * @param string $font_family Font family to test. + * @param array $variation Variation to test. + * @param string $expected Expected results. + */ + public function test_convert_variation_into_handlee( $font_family, array $variation, $expected ) { + $this->assertSame( $expected, WP_Webfonts_Utils::convert_variation_into_handle( $font_family, $variation ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_convert_variation_into_handle() { + return array( + 'with only font-weight' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-weight' => '400', + ), + 'expected' => 'merriweather-400', + ), + 'with no font-style' => array( + 'font_family' => 'source-sans-pro', + 'variation' => array( + 'font-weight' => '200 900', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'provider' => 'local', + ), + 'expected' => 'source-sans-pro-200-900', + ), + 'with font family name and full variant' => array( + 'font_family' => 'source-sans-pro', + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected' => 'source-sans-pro-200-900-normal', + ), + ); + } + + /** + * @dataProvider data_convert_variation_into_handle_with_invalid_variation + * + * @covers WP_Webfonts_Utils::convert_variation_into_handle + * + * @param string $font_family Font family to test. + * @param array $invalid_input Variation to test. + */ + public function tests_convert_variation_into_handle_with_invalid_variation( $font_family, $invalid_input ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' ); + + $this->assertNull( WP_Webfonts_Utils::convert_variation_into_handle( $font_family, $invalid_input ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_convert_variation_into_handle_with_invalid_variation() { + return array( + 'with no font-weight or font-style' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'provider' => 'local', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + ), + 'with non-string font-weight' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-weight' => 400, + ), + ), + 'with non-string font-style' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-style' => 0, + ), + ), + 'with empty string font-weight' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-weight' => '', + ), + ), + 'with empty string font-style' => array( + 'font_family' => 'merriweather', + 'variation' => array( + 'font-style' => '', + ), + ), + ); + } + + /** + * @dataProvider data_get_font_family_from_variation + * + * @covers WP_Webfonts_Utils::convert_variation_into_handle + * + * @param array $variation Variation to test. + * @param string $expected Expected results. + */ + public function test_get_font_family_from_variation( array $variation, $expected ) { + $this->assertSame( $expected, WP_Webfonts_Utils::get_font_family_from_variation( $variation ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_font_family_from_variation() { + return array( + 'keyed by font-family' => array( + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected' => 'Source Serif Pro', + ), + 'keyed by fontFamily and as a handle' => array( + 'variation' => array( + 'fontFamily' => 'source-sans-pro', + 'font-weight' => '200 900', + 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', + 'provider' => 'local', + ), + 'expected' => 'source-sans-pro', + ), + 'with font family name and full variant' => array( + 'variation' => array( + 'provider' => 'local', + 'font-family' => 'Merriweather', + 'font-style' => 'normal', + 'font-weight' => '400 600', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected' => 'Merriweather', + ), + ); + } + + /** + * @dataProvider data_get_font_family_from_variation_with_invalid_input + * + * @covers WP_Webfonts_Utils::get_font_family_from_variation + * + * @param array $invalid_variation Variation to test. + * @param string $expected_message Expected notice message. + */ + public function test_get_font_family_from_variation_with_invalid_input( array $invalid_variation, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $this->assertNull( WP_Webfonts_Utils::get_font_family_from_variation( $invalid_variation ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_font_family_from_variation_with_invalid_input() { + return array( + 'keyed with underscore' => array( + 'variation' => array( + 'provider' => 'local', + 'font_family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not found.', + ), + 'keyed with space' => array( + 'variation' => array( + 'font family' => 'Source Sans Pro', + 'font-weight' => '200 900', + 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', + 'provider' => 'local', + ), + 'expected_message' => 'Font family not found.', + ), + 'fontFamily => empty string' => array( + 'variation' => array( + 'fontFamily' => '', + 'font-weight' => '200 900', + 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', + 'provider' => 'local', + ), + 'expected_message' => 'Font family not defined in the variation.', + ), + 'font-family => empty string' => array( + 'variation' => array( + 'provider' => 'local', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Font family not defined in the variation.', + ), + ); + } + + /** + * @dataProvider data_is_defined + * + * @covers WP_Webfonts_Utils::is_defined + * + * @param mixed $input Input to test. + */ + public function test_is_defined( $input ) { + $this->assertTrue( WP_Webfonts_Utils::is_defined( $input ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_is_defined() { + return array( + 'name: non empty string' => array( 'Some Font Family' ), + 'handle: non empty string' => array( 'some-font-family' ), + ); + } + + /** + * @dataProvider data_is_defined_when_not_defined + * + * @covers WP_Webfonts_Utils::is_defined + * + * @param mixed $invalid_input Input to test. + */ + public function test_is_defined_when_not_defined( $invalid_input ) { + $this->assertFalse( WP_Webfonts_Utils::is_defined( $invalid_input ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_is_defined_when_not_defined() { + return array( + 'empty string' => array( '' ), + 'string 0' => array( '0' ), + 'integer' => array( 10 ), + 'name wrapped in an array' => array( array( 'Some Font Family' ) ), + 'handle wrapped in an array' => array( array( 'some-font-family' ) ), + ); + } +}